/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* gos720 src/gos/2d/XTOP/extras/xmtexted/xmtexted.c 1.4                  */
/*                                                                        */
/* Licensed Materials - Property of IBM                                   */
/*                                                                        */
/* COPYRIGHT International Business Machines Corp. 2002,2005              */
/* All Rights Reserved                                                    */
/*                                                                        */
/* US Government Users Restricted Rights - Use, duplication or            */
/* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.      */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
static char sccsid[] = "@(#)09  1.4  src/gos/2d/XTOP/extras/xmtexted/xmtexted.c, xsample, gos720 12/27/05 15:53:03";

#include <locale.h>
#include <Xm/MainW.h>
#include <Xm/RowColumn.h>
#include <Xm/Text.h>
#include <Xm/Protocols.h>
#include <Xm/MessageB.h>
#include <Xm/FileSB.h>
#include <Xm/SelectioB.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>


typedef int (*funcptr)(char *);  /* open_file, save_file */


/* globals */
XtAppContext  app;
Widget        toplevel, textw;
char         *current_file;
Boolean       modified = False;


/* 
 * Set current filename, modified status, and titlebar string.
 */
void
set_status(char    *new_current_file,
           Boolean  new_modified)
{
  char *title;

  if (current_file != new_current_file)
    {
      if (current_file)
	XtFree(current_file);

      current_file = new_current_file ? strdup(new_current_file) : NULL;
    }

  modified = new_modified;
  
  title = strdup(current_file ? current_file : "Untitled");
  if (modified)
    {
      /* Append [modified] to the title. */
      title = (char *)XtRealloc( title, sizeof(char) *
                                 (strlen(title)+strlen(" [modified]")+1) );
      strcat(title, " [modified]");
    }

  XtVaSetValues(toplevel, XmNtitle, title, NULL);
  XtFree(title);
}


void
text_modified_cb(Widget    widget,
                 XtPointer client_data,
                 XtPointer call_data)
{
  set_status(current_file, True);
}


void
popdown_cb(Widget    widget,
           XtPointer client_data,
           XtPointer call_data)
{
  XtUnmanageChild(widget);
}


/*
 * A generic popup dialog with one "Ok" button.
 */
void
error_popup(char *msg)
{
  static Widget dialog;
  XmString      ok, msgstr, title;

  if (!dialog)
    {
      dialog = XmCreateErrorDialog(toplevel, "error_dialog", NULL, 0);

      title = XmStringCreateLocalized("Error");
      XtVaSetValues(dialog, 
                    XmNdialogTitle, title,
                    XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL,
                    NULL);         
      XmStringFree(title);

      XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));
      XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));

      XtAddCallback(dialog, XmNokCallback, popdown_cb, NULL);
    }

  msgstr = XmStringCreateLocalized(msg);
  XtVaSetValues(dialog, XmNmessageString, msgstr, NULL);
  XmStringFree(msgstr); 

  XtManageChild(dialog);
  XtPopup(XtParent(dialog), XtGrabNone);
}


int
open_file(char *filename)
{
  struct stat  stat_buffer; 
  FILE        *fd;
  char         msg[256];
  char        *contents;
  long int     bytes_read;

  /* Check for existence, proper file type, and readability. */
  if ( (stat(filename, &stat_buffer) == -1) ||
       ((stat_buffer.st_mode & S_IFMT) != S_IFREG) ||
       (!(fd = fopen(filename, "r"))) ) 
    {
      snprintf(msg, 255, "Unable to open %s.", filename);
      msg[255] = 0;
      error_popup(msg);     
      return(0);
    }

  if ( !(contents = XtMalloc(stat_buffer.st_size+1)) )
    {
      snprintf(msg, 255, "Unable to allocate %ld bytes for %s.", 
               stat_buffer.st_size+1, filename);
      msg[255] = 0;
      error_popup(msg);     
      fclose(fd);
      return(0);
    }
  
  bytes_read = fread(contents, sizeof(char), stat_buffer.st_size, fd);

  /* Check for short read. */
  if (bytes_read != stat_buffer.st_size)
    {
      snprintf(msg, 255, "Error reading %s: entire file not read.", filename);
      msg[255] = 0;
      error_popup(msg);     
    }

  contents[bytes_read] = 0;  /* null terminate */
  XmTextSetString(textw, contents);

  XtFree(contents);
  fclose(fd);

  set_status(filename, False);
  return(1);
}


int
save_file(char *filename)
{
  FILE *fd;
  char  msg[256];
  char *contents;

  /* Check for write ability. */
  if ( !(fd = fopen(filename, "w")) )
    {
      snprintf(msg, 255, "Unable to save %s.", filename);
      msg[255] = 0;
      error_popup(msg);
      return(0);
    }

  contents = XmTextGetString(textw);

  /* Check for short write. */
  if (fwrite(contents, sizeof(char), strlen(contents), fd) != strlen(contents))
    {
      snprintf(msg, 255, "Error writing %s: entire file not saved.", filename);
      msg[255] = 0;
      error_popup(msg);
    }

  XtFree(contents);
  fclose(fd);

  set_status(filename, False);
  return(1);
}


/* 
 * The window manager has sent WM_DELETE_WINDOW.
 */
void 
delete_window_cb(Widget widget,
                 XtPointer client_data,
                 XtPointer call_data)
{
  if (check_saved_dialog())
    exit(0);
}
    
                  
/*
 * "Ok" has been clicked on the "Save As" dialog.
 */
void
file_save_cb(Widget    widget,
             XtPointer client_data,
             XtPointer call_data)
{
  XmFileSelectionBoxCallbackStruct *cbs =
    (XmFileSelectionBoxCallbackStruct *)call_data;
  int  *retval = (int *)client_data;
  char *filename;

  if (!XmStringGetLtoR(cbs->value, XmFONTLIST_DEFAULT_TAG, &filename))
    return;
  
  if (*retval = save_file(filename))
    XtUnmanageChild(widget);

  XtFree(filename);
}


/*
 * "Ok" has been clicked on the "Open File" dialog.
 */
void
file_open_cb(Widget    widget,
             XtPointer client_data,
             XtPointer call_data)
{
  XmFileSelectionBoxCallbackStruct *cbs =
    (XmFileSelectionBoxCallbackStruct *)call_data;
  int  *retval = (int *)client_data;
  char *filename;

  if (!XmStringGetLtoR(cbs->value, XmFONTLIST_DEFAULT_TAG, &filename))
    return;
  
  if (*retval = open_file(filename))
    XtUnmanageChild(widget);

  XtFree(filename);
}


/*
 * Callback for the "Are you sure?" dialog.
 */
void
check_saved_cb(Widget    widget,
               XtPointer client_data,
               XtPointer call_data)
{
  XmAnyCallbackStruct *cbs = (XmAnyCallbackStruct *)call_data;
  int                 *retval = (int *)client_data;
  
  switch(cbs->reason)
    {
      case XmCR_OK: /* Yes */
        *retval = 1;
        break;
 
      case XmCR_CANCEL: /* No */
        *retval = 2;
        break;

      default: /* Cancel */
        *retval = 0;
        break;
    }
}


/*
 * Generic file selection dialog.  
 * Returns 1 if open or save was successfully performed, 0 otherwise.
 */
int
file_select_dialog(funcptr open_or_save)
{
  static Widget open_dialog, save_dialog;
  Widget        dialog;
  XmString      title, ok_str;
  int           retval = -1;

  dialog = (open_or_save == open_file) ? open_dialog : save_dialog;

  if (!dialog)
    {
      dialog = XmCreateFileSelectionDialog(toplevel, "file_selection", 
                                           NULL, 0);
      
      if (open_or_save == open_file)
        {
          title = XmStringCreateLocalized("Open File");
          ok_str = XmStringCreateLocalized("Open");
          XtAddCallback(dialog, XmNokCallback, file_open_cb, &retval);
        }
      else
        {
          title = XmStringCreateLocalized("Save As");
          ok_str = XmStringCreateLocalized("Save");
          XtAddCallback(dialog, XmNokCallback, file_save_cb, &retval);
        }

      XtVaSetValues(dialog, 
                    XmNdialogTitle,   title,
                    XmNdialogStyle,   XmDIALOG_FULL_APPLICATION_MODAL,
                    XmNokLabelString, ok_str,
                    NULL);         

      XtUnmanageChild(XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
      
      XmStringFree(title);
      XmStringFree(ok_str);
    }

  XtAddCallback(dialog, XmNcancelCallback, popdown_cb, NULL);

  XtManageChild(dialog);
  XtPopup(XtParent(dialog), XtGrabNone);

  while (retval == -1)
    XtAppProcessEvent(app, XtIMAll);
    
  return(retval);
}


/*
 * Prompt user before wiping a modified document.
 * Returns 1 (saved), 2 (didn't save), or 0 (canceled).
 */
int
check_saved_dialog(void)
{
  static Widget dialog;
  XmString      title, msg, yes, no, cancel;
  int           retval = -1;

  if (!modified)
    return(1);

  if (!dialog)
    {
      title = XmStringCreateLocalized("Warning");
      msg = XmStringCreateLocalized("The current document has been modified.\n"
                                    "Would you like to save it?");
      yes = XmStringCreateLocalized("Yes");
      no = XmStringCreateLocalized("No");
      cancel = XmStringCreateLocalized("Cancel");

      dialog = XmCreateWarningDialog(toplevel, "check_saved", NULL, 0);

      XtVaSetValues(dialog, 
                    XmNdialogTitle,       title,
                    XmNdialogStyle,       XmDIALOG_FULL_APPLICATION_MODAL,
                    XmNdefaultButtonType, XmDIALOG_HELP_BUTTON,
                    XmNmessageString,     msg,
                    XmNokLabelString,     yes,
                    XmNcancelLabelString, no,
                    XmNhelpLabelString,   cancel,
                    NULL);

      XmStringFree(title);
      XmStringFree(msg);
      XmStringFree(yes);
      XmStringFree(no);
      XmStringFree(cancel);
    }

  XtAddCallback(dialog, XmNokCallback,     check_saved_cb, &retval);
  XtAddCallback(dialog, XmNcancelCallback, check_saved_cb, &retval);
  XtAddCallback(dialog, XmNhelpCallback,   check_saved_cb, &retval);

  XtManageChild(dialog);
  XtPopup(XtParent(dialog), XtGrabNone);

  while (retval == -1)
    XtAppProcessEvent(app, XtIMAll);

  if (retval == 1) /* Yes */
    {
      /* Now success depends on the save operation. */
      retval = (current_file) ? save_file(current_file) :
                                file_select_dialog(save_file);
    }

  XtPopdown(XtParent(dialog));
  return(retval);
}


/* 
 * A menu item from the "File" pulldown was selected.
 */
void
file_menu_cb(Widget    widget,
             XtPointer client_data,
             XtPointer call_data)
{
  int   item = (int)client_data;
  char *filename;

  switch(item)
    {
      case 0: /* New */
        if (check_saved_dialog())
          {
            XmTextSetString(textw, "");
            set_status(NULL, False);
          }
        break;

      case 1: /* Open */
        if (check_saved_dialog())
          (void)file_select_dialog(open_file);
        break;
        
      case 2: /* Save */
        if (current_file)
          {
            save_file(current_file);
            break;
          }
        /* else fall through */

      case 3: /* Save As */
        (void)file_select_dialog(save_file);
        break;

      case 4:  /* Quit */
        if (check_saved_dialog())
          exit(0);
        break;

      default: 
        break;
    }
}


int 
main(int argc, char* argv[])
{
  Widget       mainw, menubar;
  XmString     file; 
  XmString     new, open, save, saveas, quit;
  XmString     new_acc, open_acc, save_acc, quit_acc;
  Atom         wm_delete_window;
  Arg          args[1];

  setlocale(LC_ALL, "");

  toplevel = XtVaOpenApplication(&app, "XmTextEd", NULL, 0, &argc, argv,
                                 NULL, sessionShellWidgetClass, NULL);

  set_status(NULL, False);  /* no current file, unmodified */

  /* Callback for when the window manager sends WM_DELETE_WINDOW. */
  wm_delete_window = XmInternAtom(XtDisplay(toplevel),
                                  "WM_DELETE_WINDOW", False);
  XmAddWMProtocols(toplevel, &wm_delete_window, 1);
  XmAddWMProtocolCallback(toplevel, wm_delete_window, delete_window_cb, NULL);

  mainw = XtVaCreateWidget("mainw", xmMainWindowWidgetClass, toplevel, NULL);

  file = XmStringCreateLocalized("File");
  menubar = XmVaCreateSimpleMenuBar(mainw, "menubar",
                                    XmVaCASCADEBUTTON, file, 'F',
                                    NULL);
  XmStringFree(file);

  new = XmStringCreateLocalized("New");
  new_acc = XmStringCreateLocalized("Ctrl+N");
  open = XmStringCreateLocalized("Open");
  open_acc = XmStringCreateLocalized("Ctrl+O");
  save = XmStringCreateLocalized("Save");
  save_acc = XmStringCreateLocalized("Ctrl+S");
  saveas = XmStringCreateLocalized("Save As");
  quit = XmStringCreateLocalized("Quit");
  quit_acc = XmStringCreateLocalized("Ctrl+Q");

  XmVaCreateSimplePulldownMenu(menubar, "file", 0, file_menu_cb,
    XmVaPUSHBUTTON, new,    'N', "Ctrl<Key>n", new_acc,
    XmVaPUSHBUTTON, open,   'O', "Ctrl<Key>o", open_acc,
    XmVaSEPARATOR,
    XmVaPUSHBUTTON, save,   'S', "Ctrl<Key>s", save_acc,
    XmVaPUSHBUTTON, saveas, 'A', NULL,         NULL,
    XmVaSEPARATOR,
    XmVaPUSHBUTTON, quit,   'Q', "Ctrl<Key>q", quit_acc,
    NULL);

  XmStringFree(new);
  XmStringFree(new_acc);
  XmStringFree(open);
  XmStringFree(open_acc);
  XmStringFree(save);
  XmStringFree(save_acc);
  XmStringFree(saveas);
  XmStringFree(quit);

  XtSetArg(args[0], XmNeditMode, XmMULTI_LINE_EDIT);
  textw = XmCreateScrolledText(mainw, "textw", args, 1);
  XtAddCallback(textw, XmNvalueChangedCallback, text_modified_cb, NULL);

  XtManageChild(textw);
  XtManageChild(menubar);
  XtManageChild(mainw);
  XtRealizeWidget(toplevel);

  /* Check for initial file specified on command line. */
  if ( (argc>1) && argv[1] && *argv[1] )
    open_file(argv[1]);

  XtAppMainLoop(app);
}
