/*****************************************************************************
 * xbc.c: An X interface to bc
 *
 *         From:
 *                   The X Window System, 
 *            Programming and Applications with Xt
 *                   OPEN LOOK Edition
 *         by
 *              Douglas Young & John Pew
 *              Prentice Hall, 1991
 *
 *              Example described on pages: 
 *
 *
 *  Copyright 1991 by Prentice Hall
 *  All Rights Reserved
 *
 * This code is based on the OPEN LOOK Intrinsics Toolkit (OLIT) and 
 * the X Window System
 *
 * Permission to use, copy, modify, and distribute this software for 
 * any purpose and without fee is hereby granted, provided that the above
 * copyright notice appear in all copies and that both the copyright notice
 * and this permission notice appear in supporting documentation.
 *
 * Prentice Hall and the authors disclaim all warranties with regard to 
 * this software, including all implied warranties of merchantability and 
 * fitness.
 * In no event shall Prentice Hall or the authors be liable for any special,
 * indirect or consequential damages or any damages whatsoever resulting from 
 * loss of use, data or profits, whether in an action of contract, negligence 
 * or other tortious action, arising out of or in connection with the use 
 * or performance of this software.
 *
 * OPEN LOOK is a trademark of UNIX System Laboratories.
 * X Window System is a trademark of the Massachusetts Institute of Technology
 ****************************************************************************/

#include <stdio.h>
#include <ctype.h>
#include <X11/StringDefs.h>
#include <X11/Intrinsic.h>
#include <Xol/OpenLook.h>
#include <Xol/OblongButt.h>
#include <Xol/ControlAre.h>
#include <Xol/Form.h>
#include <Xol/TextEdit.h>
#include "libXs.h"

Widget     display;
Widget     create_button();
void       quit_bc();
void       get_from_bc();
void       send_to_bc();

main(argc, argv)
  int       argc;
  char     *argv[];
{
  Widget    toplevel, panel, keyboard, qbutton;
  Arg       wargs[10];
  int       n;

  toplevel = OlInitialize(argv[0], "Xbc", NULL, 0, &argc, argv);
  /*
   * Create a ControlArea widget as a base for the 
   * rest of the calculator.
   */
  n = 0; 
  XtSetArg(wargs[n], XtNlayoutType, OL_FIXEDCOLS); n++;
  XtSetArg(wargs[n], XtNmeasure, 1); n++;
  panel = XtCreateManagedWidget("panel", 
                                controlAreaWidgetClass,
                                toplevel, wargs, n);
  /*
   * Create the calculator display.
   */
  n = 0; 
  XtSetArg(wargs[n], XtNlinesVisible, (XtArgVal) 1); n++;
  XtSetArg(wargs[n], XtNborderWidth, (XtArgVal) 1); n++;
  display = XtCreateManagedWidget("display", textEditWidgetClass,
                                  panel, wargs, n);
  /*
   * Make the keyboard, which manages 4 columns of buttons
   */
  n = 0; 
  XtSetArg(wargs[n], XtNlayoutType, OL_FIXEDCOLS); n++;
  XtSetArg(wargs[n], XtNsameSize, OL_ALL); n++;
  XtSetArg(wargs[n], XtNmeasure, 4); n++;
  keyboard = XtCreateManagedWidget("keyboard", 
                                   controlAreaWidgetClass,
                                   panel, wargs, n);
  /* 
   * Create the keyboard buttons. This order makes it 
   * look like a typical desktop calculator.
   */
  create_button("1", keyboard);
  create_button("2", keyboard);
  create_button("3", keyboard);
  create_button("+", keyboard);
  create_button("4", keyboard);
  create_button("5", keyboard);
  create_button("6", keyboard);
  create_button("-", keyboard);
  create_button("7", keyboard);
  create_button("8", keyboard);
  create_button("9", keyboard);
  create_button("*", keyboard);
  create_button("0", keyboard);
  create_button(".", keyboard);
  create_button("=", keyboard);
  create_button("/", keyboard);
  /*
   *  Add a callback that tells bc to exit.
   */
  OlAddCallback(toplevel, XtNwmProtocol, quit_bc, NULL);
  /* 
   * Add callback get_from_bc() --  invoked when input 
   * is available from stdin.
   */
  XtAddInput(fileno(stdin), XtInputReadMask, 
             get_from_bc, display);
  /* 
   * Exec the program "bc" and set up pipes 
   * between it and us.
   */
  xs_talkto("bc");

  XtRealizeWidget(toplevel);
  XtMainLoop();
}

Widget
create_button(name, parent)
  char    *name;
  Widget   parent;
{
  extern void send_to_bc();
  Widget      button;  

  /*
   * Create a single button and attach an activate callback.
   */
  button = XtCreateManagedWidget(name, oblongButtonWidgetClass,
                                 parent, NULL, 0);
  XtAddCallback(button, XtNselect, send_to_bc, name);
  return (button);
}

void
quit_bc(w, client_data, call_data)
  Widget     w;
  XtPointer  client_data;
  XtPointer  call_data;
{
  OlWMProtocolVerify *olwmpv = (OlWMProtocolVerify *)call_data;

  if(olwmpv->msgtype == OL_WM_DELETE_WINDOW) {
    /*
     * Tell bc to quit.
     */
    fprintf(stdout, "quit\n");
    exit(0);
  }
}

void
send_to_bc(w, client_data, call_data)
  Widget    w;
  XtPointer client_data;
  XtPointer call_data;
{
  char *buffer = (char *)client_data;
  static int  start_new_entry = TRUE;
  char *copybuffer;

  /*
   * If this is the beginning of a new operand, 
   * clear the display.
   */
  if(start_new_entry){
    reset_display();
    start_new_entry = FALSE;
  }
  switch (buffer[0]) {
  /*
   * If the user entered and '=', send bc a newline, clear
   * the display, and get ready for a new operand.
   */
  case '=':
    OlTextEditCopyBuffer(display, &copybuffer);
    fprintf(stdout, "%s", copybuffer);
    XtFree(copybuffer);
    fprintf(stdout, "\n");
    reset_display();
    start_new_entry = TRUE;
    break;
  /*
   * If this is an operator, get the previous operand
   * from the display buffer, and send it to bc before 
   * sending the operand.
   */
  case '-':
  case '+':
  case '/':
  case '*':
  case '^':
    OlTextEditCopyBuffer(display, &copybuffer);
    fprintf(stdout, "%s", copybuffer);
    fprintf(stdout, "%c", buffer[0]);
    XtFree(copybuffer);
    reset_display();
    break;
  /*
   * Anything else must be a digit, so append it to the
   * display buffer.
   */
  default:
    xs_insert_string(display, buffer);
  }
  fflush(stdout);
}

void
get_from_bc(client_data, fid, id)
  XtPointer    client_data;
  int         *fid;
  XtInputId   *id;
{
  Widget w = (Widget)client_data;
  char   buf[BUFSIZ];
  int    nbytes, i;

  /* 
   * Get all pending input and append it to the display 
   * widget. Discard lines that begin with a newline.
   */
  nbytes = read(*fid, buf, BUFSIZ);
  if (nbytes && buf[0] != '\n') {
  /*
   * Null terminate the string at the first newline,
   * or at the end of the bytes read.
   */
   for(i=0;i<nbytes;i++)
     if(buf[i] == '\n')
         buf[i] = '\0';
     buf[nbytes] = '\0'; 
     xs_insert_string(display, buf);
  }
}

reset_display()
{
  /*
   * Clear the text buffer and go to position 1.
   */
  if(OlTextEditClearBuffer((TextEditWidget)display) == FALSE) {
    fprintf(stderr,"OlTextEditClearBuffer() failed\n");
  }
}
