/* w6a.c   extracted from OSF's xmeditor.c, then ANSI C, then clean up  */

#include <stdlib.h>
#include <stdio.h>
#include <Xm/Xm.h>
#include <Xm/CascadeB.h>
#include <Xm/DialogS.h>
#include <Xm/BulletinB.h>
#include <Xm/FileSB.h>
#include <Xm/MainW.h>
#include <Xm/MessageB.h>
#include <Xm/Label.h>
#include <Xm/PushB.h>
#include <Xm/RowColumn.h>
#include <Xm/SelectioB.h>
#include <Xm/Text.h>

enum which {MENU_EXIT, MENU_OPEN, MENU_NEW, MENU_CLOSE, MENU_SAVE_AS,
            MENU_PRINT, DIALOG_FSELECT, DIALOG_CWARNING, DIALOG_XWARNING,
            DIALOG_NEW, DIALOG_SAVE, DIALOG_PRINT};

Widget text;			/* multi-line text widget		    */
Widget open_dialog;		/* file selection dialog 		    */
Widget new_dialog;		/* file name prompt dialog 		    */
Widget close_warning;		/* special internal selection dialog	    */
Widget exit_warning;		/* special internal selection dialog	    */
Widget general_warning;		/* warning dialog	 		    */
Widget save_dialog;		/* save as prompt dialog	 	    */
Widget print_warning;		/* warning dialog		 	    */
Boolean file_saved = True;	/* indicates that the present file is saved */
char *filename = NULL;		/* string containing file name 		    */
int start_pos, end_pos;		/* start and end position of last action    */

XmStringCharSet charset = (XmStringCharSet) XmSTRING_DEFAULT_CHARSET;
			/* shorter name, charset, used to set up XmStrings */


/* OpenFile, Open the present file.  Returns true if file exists and
   open is sucessful. */
static Boolean OpenFile(void)
{
   int file_length;		/* Length of file. 	  */
   char * file_string;		/* Contents of file. 	  */
   FILE *fp = NULL;		/* Pointer to open file   */
   
   if ((fp = fopen(filename, "r+")) == NULL)
	if ((fp = fopen(filename, "r")) != NULL) {
	    fprintf(stderr, "Warning: file opened read only.\n");
	} else {
	    return(False);
	}

/*   if (stat(filename, &statbuf) == 0)
	 file_length = statbuf.st_size;
   else   find POSIX way to get length of a file */
	 file_length = 1000000; /* arbitrary file length for now */

#ifdef VMS
        { char *p;
          p = strchr(filename,';');
          if(p != NULL) *p = '\0';
        }
#endif
   /* read the file string */
   file_string = (char *) XtMalloc((unsigned)file_length);
   fread(file_string, sizeof(char), file_length, fp);
   file_length = strlen(file_string); /* new have actual length */

   /* close up the file */
   if (fclose(fp) != 0) fprintf(stderr, "Warning: unable to close file.\n");

   /* added the file string to the text widget */
   XmTextSetString(text, file_string);

   file_saved = True; /* intialize to True */
	 
   /* make appropriate item sensitive */
   XtSetSensitive(text, True);

   return(True);
}


/* Save call back goes here, utility routine SaveFile()
   below does most of work */


/* SaveFile, Save the present file. */
static Boolean SaveFile(void)
{
    char * file_string = NULL;		   /* Contents of file.		      */
    FILE *tfp;				   /* Pointer to open temporary file. */
	char	namebuf[BUFSIZ]; /* for "system" call below */
	int		status;
    char *tempname = (char *)XtMalloc(13); /* Temporary file name. 	      */

    strcpy(tempname, "xmedit_temp");
    
    if ((tfp = fopen(tempname, "w")) == NULL) {
       fprintf(stderr, "Warning: unable to open temp file, text not saved.\n");
       return(False);
    }

    /* get the text string */
    file_string = (char *)XmTextGetString(text);

    /* write to a temp file */
    fwrite(file_string, sizeof(char), strlen(file_string) + 1, tfp);

    /* flush and close the temp file */
    if (fflush(tfp) != 0) fprintf(stderr,"Warning: unable to flush file.\n");
    if (fclose(tfp) != 0) fprintf(stderr,"Warning: unable to close file.\n");

    if (file_string != NULL) {
        XtFree(file_string); /* free the text string */
    }

    /* move the tempname to the saved file, but do it independent
	   of filesystem boundaries */
#ifdef VMS
	sprintf (namebuf, "copy %s %s\0", tempname, filename);
#else
	sprintf (namebuf, "cp %s %s\0", tempname, filename);
#endif
	status = system(namebuf);
#ifndef VMS
	unlink (tempname); 
#endif
	if (status == 0  || status == 1) {
        file_saved = True;
    } else {
        fprintf(stderr, "Warning: unable to save file.\n");
        XtFree(tempname);
        return(False);
    }
           
    XtFree(tempname);
    return(True);
}


/* CloseFile, Close the present file. */
static void CloseFile(void)
{
    /* blank out the text string in the text widget.
       caution: is causes a value changed callack. */
    XmTextSetString(text, "");

    file_saved = True; /* reinitialize file_saved flag */

    /* free the file name */
    if (filename != NULL) {
	XtFree(filename);
        filename = NULL;
    }

    /* set text to insensitive */
    XtSetSensitive(text, False);
}


/* PrintFile, Write the file to disk, use system command to print it. */
static void PrintFile(void)
{
	char *command;		/* command used in printing */

	/* malloc space for the command name. 
	   Note: command = size of the filename +
	   "lp " + null terminator */
#ifdef VMS
	command = XtMalloc(strlen(filename) + 7);
	sprintf(command, "print %s\0", filename);
#else
	command = XtMalloc(strlen(filename) + 5);
	sprintf(command, "lpr %s\0", filename);
#endif
	if (system(command) != 0)
		fprintf(stderr, "print failed");
	XtFree(command);
}


/* FileChangedCB, Process callback from Text. */
void FileChangedCB (Widget w, XtPointer client_data, XtPointer call_data) 
{
    /* set the file_saved flag to indicate that the
       file has been modified and the user should be
       notified before exiting. */

    file_saved = False;
}


/* MenuCB, Process callback from PushButtons in PulldownMenus. */
void MenuCB (Widget w,
                    enum  which client_data,
                    XtPointer call_data) 
{
	register int ac;		/* arg count		    */
	Arg al[10];			/* arg list		    */

	switch (client_data)
	{
		case MENU_OPEN:
			/* display the file selection dialog */
			XtManageChild (open_dialog);
			break;

		case MENU_NEW:
			/* display the prompt dialog */
			XtManageChild (new_dialog);
			break;

		case MENU_CLOSE:
	 		/* the present file has not been saved since
			   the last modification */
			if (!file_saved) /* display the 'save' message dialog */
			   XtManageChild (close_warning);
			else
			   CloseFile();
			break;

		case MENU_SAVE_AS:
			/* Display the 'save as' dialog with the
			   present filename displayed in it. */
			ac = 0;
			XtSetArg(al[ac], XmNtextString,
			     XmStringCreateLtoR (filename, charset));  ac++;
			XtSetValues(save_dialog, al, ac);
			XtManageChild (save_dialog);
			break;

		case MENU_PRINT:
			if (!file_saved)
			   XtManageChild(print_warning);
			else if (filename != NULL)
                           PrintFile();
			break;

		case MENU_EXIT:
			/* exit if there is no files open */
			if (!file_saved) /* display the 'save' message dialog */
			   XtManageChild (exit_warning);
			else {
			   /* close up file pointers and descriptors */
			   CloseFile();

			   /* exit this program */
			   exit(EXIT_SUCCESS);
			}
			break;

		default:
			/* unknown client_data was recieved and
			   there is no setup to handle this */
			fprintf(stderr, "Warning: in menu callback\n");
			break;
	}
}


/* DialogApplyCB, Process callback from Dialog apply actions. */
void DialogApplyCB (Widget w,
                           enum which client_data,
                           XtPointer call_data) 
{
	switch (client_data)
	{
		case DIALOG_PRINT:
			if (filename != NULL) 
                            PrintFile();
                        break;

		case DIALOG_CWARNING:
			CloseFile();
			file_saved = True; /* reset the default */
			break;

		case DIALOG_XWARNING:
			CloseFile();
			exit(EXIT_SUCCESS);
			break;		

		default:
			/* unknown client_data was recieved and
			   there is no setup to handle this */
			fprintf (stderr, "Warning: in apply callback\n");
			break;
	}
}


/* DialogCancelCB, Process callback from Dialog cancel actions. */
void DialogCancelCB (Widget w,
                            enum which client_data,
                            XtPointer call_data) 
{
	switch (client_data)
	{
		case DIALOG_FSELECT:
			/* popdown the file selection box */
		  	XtUnmanageChild (open_dialog);
			break;

		case DIALOG_CWARNING:
		case DIALOG_XWARNING:
		case DIALOG_NEW:
		case DIALOG_PRINT:
			/* no action is necessary at this time */
			break;

		default:
			/* a unknown client_data was recieved and
			   there is no setup to handle this */
			fprintf (stderr, "Warning: in cancel callback\n");
			break;
	}
}


/* DialogAcceptCB, Process callback from Dialog actions. */
void DialogAcceptCB (Widget w,
                            enum which client_data,
                            XtPointer call_data) 
{

	switch (client_data)
	{
		case DIALOG_FSELECT:
		        /* open the file and read it into the text widget */
			if (filename != NULL) {
			   XtFree(filename);
			   filename = NULL;
                        }
			{
			   XmFileSelectionBoxCallbackStruct *fcb =
				 (XmFileSelectionBoxCallbackStruct *) call_data;

			   /* get the filename from the file selection box */
			   XmStringGetLtoR(fcb->value, charset, &filename);

			   /* Open file, print error if it does not exist. */
			   if (!OpenFile())
	   		      fprintf(stderr, "Warning: unable to open file\n");

			   /* popdown the file selection box */
		  	   XtUnmanageChild (open_dialog);
			}
			break;

		case DIALOG_NEW:
		        /* open the file and read it into the text widget */
			if (filename != NULL) {
			   XtFree(filename);
			   filename = NULL;
                        }
			{
			   XmSelectionBoxCallbackStruct *scb =
				 (XmSelectionBoxCallbackStruct *) call_data;

			   /* get the filename string from the file
			      name prompt box */
			   XmStringGetLtoR(scb->value, charset, &filename);

			   /* open file if it exists,
			      if not set items sensitive */
			   if (!OpenFile()) {
   			   	/* make appropriate item sensitive */
   			   	XtSetSensitive(text, True);
			   }
			   /* popdown the file selection box */
		  	   XtUnmanageChild (new_dialog);
			}
			break;

		case DIALOG_CWARNING:
			/* save the file */
			if (SaveFile()) {
			   CloseFile(); /* close the file */
			} else
			   fprintf(stderr, 
			       "Warning: unable to save file, file not closed");
			break;

		case DIALOG_XWARNING:
			/* save the file */
			if (SaveFile()) {
			   CloseFile(); /* close the file */
			   exit(EXIT_SUCCESS);
                        } else
			   fprintf(stderr,
				 "Warning: unable to save file, exit aborted");
			break;

		case DIALOG_SAVE:
			{
			   XmSelectionBoxCallbackStruct *scb =
				 (XmSelectionBoxCallbackStruct *) call_data;

			   /* get the filename string from the file
			      selection box */
			   XmStringGetLtoR(scb->value, charset, &filename);
			   SaveFile();
			   XtUnmanageChild (save_dialog);
			}
			break;

		case DIALOG_PRINT:
			/* save the file */
			if (SaveFile()) {
			   if (filename != NULL) 
                              PrintFile();
			} else
			   fprintf(stderr, 
			      "Warning: unable to save file, file not printed");
			break;

		default:
			/* unknown callback type */
			fprintf (stderr, "Warning: in accept callback\n");
			break;
	}
}


/* CreateSpecialWarningDialog, Create special 4 button message box out of a
   Selection box. */
static Widget CreateSpecialWarningDialog (Widget parent,
                                          String  name,
                                          String  image_string,
                                          String  message,
					  ArgList arglist,
                                          int  argcount)
{
	Widget 		warning_dialog;	/*  special warning selection box */
	Widget 		work_area;	/*  rowcolumn for pixmap and text */
	Widget 		pixmap_label;	/*  pixmap label 		  */
	Widget 		text_label;	/*  text label 			  */
	Widget 		apply_button;	/*  apply button "Discard"        */
	Widget 		ok_button;	/*  ok button "Save"              */
        Widget          kid[5];         /*  buttons		          */
	Pixel		foreground;	/*  dialog foreground		  */
	Pixel		background;	/*  dialog background		  */
	Pixmap		pixmap;		/*  dialog pixmap		  */
        register int    i;              /*  kid index			  */
        Arg             al[10];         /*  arg list		          */
        register int    ac;             /*  arg count		          */

	warning_dialog = XmCreatePromptDialog(parent, name, arglist, argcount);

	ac = 0;
	XtSetArg(al[ac], XmNorientation, XmHORIZONTAL); ac++;
	work_area = XmCreateRowColumn(warning_dialog, "workarea", al, ac);
	XtManageChild(work_area);

	ac = 0;
	XtSetArg(al[ac], XmNforeground, &foreground); ac++;
	XtSetArg(al[ac], XmNbackground, &background); ac++;
	XtGetValues(warning_dialog, al, ac);

	ac = 0;
	XtSetArg(al[ac], XmNlabelType, XmPIXMAP); ac++;
	pixmap = XmGetPixmap(XtScreen(warning_dialog), image_string,
			     foreground, background);
	XtSetArg(al[ac], XmNlabelPixmap, pixmap); ac++;
	pixmap_label = XmCreateLabel(work_area, "pixmap_label", al, ac);
	XtManageChild(pixmap_label);

	ac = 0;
	XtSetArg(al[ac], XmNlabelString,
		 XmStringCreateLtoR(message, charset)); ac++;
	text_label = XmCreateLabel(work_area, "text_label", al, ac);
	XtManageChild(text_label);

        apply_button = XmSelectionBoxGetChild (warning_dialog,
							 XmDIALOG_APPLY_BUTTON);
	
	ac = 0;
	XtSetArg(al[ac], XmNlabelString,
		 XmStringCreateLtoR("Discard", charset)); ac++;
	XtSetValues(apply_button, al, ac);
	XtManageChild(apply_button);

        ok_button = XmSelectionBoxGetChild (warning_dialog,
							 XmDIALOG_OK_BUTTON);
	ac = 0;
	XtSetArg(al[ac], XmNlabelString,
		 XmStringCreateLtoR("Save", charset)); ac++;
	XtSetValues(ok_button, al, ac);
	
        /*      Unmanage unneeded children.         */
        i = 0;
        kid[i++] = XmSelectionBoxGetChild (warning_dialog, XmDIALOG_TEXT);
        kid[i++] = XmSelectionBoxGetChild (warning_dialog,
						     XmDIALOG_SELECTION_LABEL);
        XtUnmanageChildren (kid, i);
	return(warning_dialog);
}


/* CreateMenuBar, Create MenuBar in MainWindow */
static Widget CreateMenuBar (Widget parent)
{
	Widget		menu_bar;	/*  RowColumn	 		*/
	Widget		cascade;	/*  CascadeButton		*/
	Widget		menu_pane;	/*  RowColumn	 		*/
	Widget		button;		/*  PushButton			*/
	Arg		al[10];		/*  arg list			*/
        int		ac;		/*  arg count			*/
        
	/*	Create MenuArea. */
	ac = 0;
	menu_bar = XmCreateMenuBar (parent, "menu_bar", al, ac);

	/*	Create "Options" PulldownMenu. */
	ac = 0;
	menu_pane = XmCreatePulldownMenu (menu_bar, "menu_pane", al, ac);

	ac = 0;
	XtSetArg(al[ac], XmNlabelString,
		 XmStringCreateLtoR("Open", charset)); ac++;
	XtSetArg(al[ac], XmNmnemonic, 'O'); ac++;
	button = XmCreatePushButton (menu_pane, "Open", al, ac);
	XtAddCallback (button, XmNactivateCallback,
                       (XtCallbackProc)MenuCB, (XtPointer)MENU_OPEN);
	XtManageChild (button);

	open_dialog = XmCreateFileSelectionDialog(menu_pane,
			   "file selection dialog", NULL, 0);

	XtAddCallback (open_dialog, XmNokCallback,
                       (XtCallbackProc)DialogAcceptCB, (XtPointer)DIALOG_FSELECT);
	XtAddCallback (open_dialog, XmNcancelCallback,
                       (XtCallbackProc)DialogCancelCB, (XtPointer)DIALOG_FSELECT);

	ac = 0;
	XtSetArg(al[ac], XmNlabelString,
		 XmStringCreateLtoR("New", charset)); ac++;
	XtSetArg(al[ac], XmNmnemonic, 'N'); ac++;
	button = XmCreatePushButton (menu_pane, "New", al, ac);
	XtAddCallback (button, XmNactivateCallback,
                       (XtCallbackProc)MenuCB, (XtPointer)MENU_NEW);
	XtManageChild (button);

	ac = 0;
	XtSetArg(al[ac], XmNselectionLabelString, XmStringCreateLtoR
	   ("Enter name of new file.", charset));  ac++;
	new_dialog = XmCreatePromptDialog(menu_pane,
			   "new file dialog", al, ac);
	XtAddCallback (new_dialog, XmNokCallback,
                       (XtCallbackProc)DialogAcceptCB, (XtPointer)DIALOG_NEW);
	XtAddCallback (new_dialog, XmNcancelCallback,
                       (XtCallbackProc)DialogCancelCB, (XtPointer)DIALOG_NEW);

	ac = 0;
	XtSetArg(al[ac], XmNlabelString,
		 XmStringCreateLtoR("Close", charset)); ac++;
	XtSetArg(al[ac], XmNmnemonic, 'C'); ac++;
	button = XmCreatePushButton (menu_pane, "Close", al, ac);
	XtAddCallback (button, XmNactivateCallback,
                       (XtCallbackProc)MenuCB, (XtPointer)MENU_CLOSE);
	XtManageChild (button);

	close_warning = CreateSpecialWarningDialog(menu_pane, "save_warning",
				      "exclam.xbm", "Save Changes?", al, ac);

	XtAddCallback (close_warning, XmNapplyCallback,
		       (XtCallbackProc)DialogApplyCB, (XtPointer)DIALOG_CWARNING);
	XtAddCallback (close_warning, XmNokCallback,
		       (XtCallbackProc)DialogAcceptCB, (XtPointer)DIALOG_CWARNING);


        /* Save  homework stuff goes here */


	ac = 0;
	XtSetArg(al[ac], XmNlabelString,
		 XmStringCreateLtoR("Save As...", charset)); ac++;
	XtSetArg(al[ac], XmNmnemonic, 'A'); ac++;
	button = XmCreatePushButton (menu_pane, "Save As...", al, ac);
	XtAddCallback (button, XmNactivateCallback,
                       (XtCallbackProc)MenuCB, (XtPointer)MENU_SAVE_AS);
	XtManageChild (button);

	ac = 0;
	XtSetArg(al[ac], XmNselectionLabelString, XmStringCreateLtoR
	   ("Save As...", charset));  ac++;
	save_dialog = XmCreatePromptDialog(menu_pane, "save dialog", al, ac);
	XtAddCallback (save_dialog, XmNokCallback,
		       (XtCallbackProc)DialogAcceptCB, (XtPointer)DIALOG_SAVE);

	ac = 0;
	XtSetArg(al[ac], XmNlabelString,
		 XmStringCreateLtoR("Print", charset)); ac++;
	XtSetArg(al[ac], XmNmnemonic, 'P'); ac++;
	button = XmCreatePushButton (menu_pane, "Print", al, ac);
	XtAddCallback (button, XmNactivateCallback,
                       (XtCallbackProc)MenuCB, (XtPointer)MENU_PRINT);
	XtManageChild (button);

	ac = 0;
	XtSetArg(al[ac], XmNselectionLabelString, XmStringCreateLtoR
	   ("Save file before printing?", charset));  ac++;
	print_warning = CreateSpecialWarningDialog(menu_pane, "print_warning",
			 "exclam.xbm", "Save file before printing?", al, ac);
	XtAddCallback (print_warning, XmNokCallback,
		       (XtCallbackProc)DialogAcceptCB, (XtPointer)DIALOG_PRINT);

	ac = 0;
	XtSetArg(al[ac], XmNlabelString,
		 XmStringCreateLtoR("Exit", charset)); ac++;
	XtSetArg(al[ac], XmNmnemonic, 'E'); ac++;
	XtSetArg(al[ac], XmNacceleratorText,
		 XmStringCreateLtoR("F3", charset)); ac++;
	XtSetArg(al[ac], XmNaccelerator, "<Key>F3:"); ac++;
	button = XmCreatePushButton (menu_pane, "Exit", al, ac);
	XtAddCallback (button, XmNactivateCallback,
                       (XtCallbackProc)MenuCB, (XtPointer)MENU_EXIT);
	XtManageChild (button);

        ac = 0;
	exit_warning = CreateSpecialWarningDialog(menu_pane, "exit warning",
				      "exclam.xbm", "Save Changes?", al, ac);
	XtAddCallback (exit_warning, XmNapplyCallback,
		       (XtCallbackProc)DialogApplyCB, (XtPointer)DIALOG_XWARNING);
	XtAddCallback (exit_warning, XmNokCallback,
                        (XtCallbackProc)DialogAcceptCB, (XtPointer)DIALOG_XWARNING);

	ac = 0;
	XtSetArg (al[ac], XmNsubMenuId, menu_pane);  ac++;
	XtSetArg(al[ac], XmNlabelString,
		 XmStringCreateLtoR("File", charset)); ac++;
	XtSetArg(al[ac], XmNmnemonic, 'F'); ac++;
	cascade = XmCreateCascadeButton (menu_bar, "File", al, ac);
	XtManageChild (cascade);

	return (menu_bar);
}


/*      main
**		Initialize toolkit.
**		Create MainWindow and subwidgets.
**		Realize toplevel widgets.
**		Process events. */
int main (int argc, char *argv[])
{
	Widget		app_shell;	/*  ApplicationShell 	*/
	Widget		main;		/*  MainWindow	 	*/
	Widget		menu_bar;	/*  RowColumn	 	*/
	Widget		form;		/*  Form		*/
	XtAppContext	app_context;	/*  Application Context */
	Arg		al[10];		/*  arg list		*/
	register int	ac;		/*  arg count		*/
	register int	i;		/*  counter		*/
	char	*progname; /* program name without the full pathname */

        void FileChangedCB();

	if (progname=strrchr(argv[0], '/'))
		progname++;
	else if (progname=strrchr(argv[0], ']'))
		progname++;
	else
		progname = argv[0];

	/* Initialize toolkit, open display and create application shell. */
        app_shell = XtVaAppInitialize(&app_context, progname,
                                      NULL, 0, &argc, argv, NULL, NULL);
 
	/*	Create MainWindow. */
        main = XtVaCreateManagedWidget("main",
	        xmMainWindowWidgetClass,   app_shell,
		XmNshadowThickness,        0,
		NULL);
  
	/*	Create MenuBar in MainWindow. */
	menu_bar = CreateMenuBar (main);
	XtManageChild (menu_bar);

	/*	Create Text. */
        ac = 0;
	XtSetArg (al[ac], XmNrows, 24);  ac++;
	XtSetArg (al[ac], XmNcolumns, 80);  ac++;
	XtSetArg (al[ac], XmNresizeWidth, False);  ac++;
	XtSetArg (al[ac], XmNresizeHeight, False);  ac++;
	XtSetArg (al[ac], XmNscrollVertical, True);  ac++;
	XtSetArg (al[ac], XmNscrollHorizontal, True);  ac++;
	XtSetArg (al[ac], XmNeditMode, XmMULTI_LINE_EDIT);  ac++;
	text = XmCreateScrolledText (main, "text", al, ac);

	/* add value changed callback */
	XtAddCallback (text, XmNmodifyVerifyCallback, 
	               (XtCallbackProc)FileChangedCB, NULL);
	XtManageChild (text);

	XmAddTabGroup(text);

	XtSetSensitive(text, False);

	/*	Realize toplevel widgets. */
	XtRealizeWidget (app_shell);

	/*	Process events. */
	XtAppMainLoop (app_context);
        return 0; /* never executed */
} /* end w6a.c file */

