/*
 * xmail - X window system interface to the mail program
 *
 * Copyright 1990,1991,1992 by National Semiconductor Corporation
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of National Semiconductor Corporation not
 * be used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 *
 * NATIONAL SEMICONDUCTOR CORPORATION MAKES NO REPRESENTATIONS ABOUT THE
 * SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE.  IT IS PROVIDED "AS IS"
 * WITHOUT EXPRESS OR IMPLIED WARRANTY.  NATIONAL SEMICONDUCTOR CORPORATION
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  IN NO
 * EVENT SHALL NATIONAL SEMICONDUCTOR CORPORATION 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.
 *
 * The following software modules were created and are Copyrighted by
 * National Semiconductor Corporation:
 *
 * 1. markIndex:
 * 2. LastNumber:
 * 3. SelectionNumber:
 * 4. SetCursor: and
 * 5. SetXY.
 *
 * Author:  Michael C. Wagnitz - National Semiconductor Corporation
 *
 */


#include <ctype.h>
#include "global.h"
#include "patchlevel.h"
#include "revtable.h"


/*
** @(#)Bell() - write a status message and (optionally) ring the terminal bell
*/
void
Bell(String msg)
{
 Arg		args[1];
 char		*p;
 char		buf[BUFSIZ];
 int		size;
 static int	worthy = 0;		/* only msgs with ending newline are */


 size = (int)strlen(msg);
 if (size >= BUFSIZ) size = BUFSIZ - 1;

 (void) strncpy(buf, msg, size);
 buf[size] = '\0';

 p = strchr(buf, '\n');			/* messages with newline get a bell */
/*
** Because we have now added enter/leave window event information messages,
** we must play a slight game with incoming messages, to prevent any error
** messages from a menu selection failure or status messages from a command
** button being overwritten by the subsequent event info messages produced
** when the menu popup is dismissed or the command completes.  To do this,
** we make the bell worthy-ness flag static, and only replace a message after
** first seeing a non-default status message, which will reset the worthy-ness
** flag, but otherwise be ignored.  When the first non-default, non-error
** message comes in, simply reset our bell worthy-ness flag and return, leaving
** the previous error message still intact.  This means, to ensure seeing the
** default message, first send a blank line to reset the worthy-ness flag.
*/
 if (! XMail.Show_Info)			/* if NOT showing info flags always */
    worthy = 0;				/* reset the bell worthy-ness flag */
 else {
    if (worthy && p == NULL) {		/* if last was but this ain't urgent */
       if (strcmp(buf, Default_Status_Info))	/* and not the default info */
          worthy = 0;			/* reset the bell worthy-ness flag */
       return;				/* ignore event info if menu errored */
      }					/* by leaving previous error message */
   }

 if (size == 0)				/* if intent was just to reset flag */
    return;

 if (p != NULL) {			/* Only display first line of output */
    *p = '\0';				/* (no new line at end of label) */
    worthy = 1;				/* this message is worthy of a bell */
   }

 if (worthy && XMail.bellRing)		/* ring bell if not silenced by user */
    XBell(XtDisplay (toplevel), 33);

 XtSetArg(args[0], XtNlabel, (XtArgVal) buf);	/* show this message text */
 XtSetValues(XtNameToWidget(toplevel, "topBox.statusWindow"), args, 1);
 XFlush(XtDisplay(toplevel));
} /* Bell */


/*
** @(#)figureWidth() - determine the figure width of the specified font
*/
int
figureWidth(XFontStruct *font)
{
 Atom		_XA_FIGURE_WIDTH;
 unsigned long	width = 0;


 _XA_FIGURE_WIDTH = XInternAtom(XtDisplay(toplevel), "FIGURE_WIDTH", FALSE);
 if ((_XA_FIGURE_WIDTH != (Atom)NULL) &&
    ((! XGetFontProperty(font, _XA_FIGURE_WIDTH, &width)) || (width == 0)))
    if (font->per_char && font->min_char_or_byte2 <= '$' &&
                          font->max_char_or_byte2 >= '$')
       width = font->per_char['$' - font->min_char_or_byte2].width;
    else
       width = font->max_bounds.width;

 return(width);
} /* end - figureWidth */



/*
** @(#)markIndex() - add or remove a tag from the start of a mail header line
**                   May be invoked by button press, which sets the insertion
**                   pointer for the text to the current mouse cursor location.
*/
void
markIndex(char *s)
{
 int			size;
 XawTextPosition	pos, old;
 String			c, p = NULL;
 char			buf[3];
 Widget			iw = XtNameToWidget(toplevel, "topBox.indexWindow");
 Arg			args[1];
 XawTextBlock		text;


 (void) strcpy(buf, s);			/* make a copy of our marker string */

 XtSetArg(args[0], XtNstring, &p);	/* retrieve the current index buffer */
 XtGetValues(iw, args, 1);

 for (pos = XawTextGetInsertionPoint(iw); pos > 0 && p[pos-1] != '\n'; pos--);

 if (buf[0] == '>') {			/* if mark is for 'current' pointer */
    XtSetArg(args[0], XtNstring, &p);	/* retrieve the current index buffer */
    XtGetValues(iw, args, 1);

    if ((int)strlen(p) < 3)			/* trap for case of no index at all */
       return;

    if ((int)strlen(p) > pos + 1)
       if (p[pos + 1] == 'S')		/* keep the Save marker if it exists */
          if ((int)strlen(buf) > 1)
             buf[1] = '\0';

    for (c = p; *c; c++)
        if (*c == '>' && (c == p || *(c - 1) == '\n')) {
           old = c - p;			/* if found, remove the old '>' mark */
           text.firstPos = 0;
           text.length   = 1;
           text.ptr      = " ";
           text.format   = FMT8BIT;
           XawTextReplace(iw, (XawTextPosition) old, (XawTextPosition) old + 1, &text);
           break;
          }
   }

 size = (int)strlen(buf);			/* now write the specified marker */
 old = (buf[0] == '>') ? pos : pos + 1;
 text.firstPos = 0;
 text.length   = size;
 text.ptr      = buf;
 text.format   = FMT8BIT;
 XawTextReplace(iw, (XawTextPosition) old, (XawTextPosition) old + size, &text);

 XawTextSetInsertionPoint(iw, pos);	/* reset our actual insertion point */
} /* markIndex */


/*
** @(#)LastNumber() - Get mail index number for last message in folder
*/
int
LastNumber(void)
{
 int			last_number;
 Arg			args[1];
 String			c, ep, str = NULL;
 Widget			iw = XtNameToWidget(toplevel, "topBox.indexWindow");
 

 XtSetArg(args[0], XtNstring, &str);
 XtGetValues(iw, args, 1);

 ep = &str[(int)strlen(str) - 1];

 for (c = ep - 1; c > str; c--)
     if (*(c - 1) == '\n' && *(c + 1) != 'D')
        break;

 for (; c < ep && !isdigit(*c); c++);
 (void) sscanf(c, "%d", &last_number);

 return(last_number);
} /* LastNumber */


/*
** @(#)SelectionNumber() - Get mail index number from line position
*/
int
SelectionNumber(int undeleting)
{
 Arg			args[1];
 String			c, str = NULL;
 Widget			iw = XtNameToWidget(toplevel, "topBox.indexWindow");
 XawTextPosition	pos;
 int			selection_number = 0;


 XtSetArg(args[0], XtNstring, &str);
 XtGetValues(iw, args, 1);

 for (pos = XawTextGetInsertionPoint(iw); pos > 0 && str[pos] != '\n'; pos--);
 pos++;

 if (! undeleting || str[pos + 1] == 'D') {
    for (c = &str[pos]; *c && !isdigit(*c); c++);
    (void) sscanf(c, "%d", &selection_number);
   }

 return(selection_number);
} /* SelectionNumber */


/* ARGSUSED */
/*
** @(#)SetCursor() - sets the wait cursor or restores the default
*/
void
SetCursor(int waiting)
   	        		/* a non-zero value sets the busy cursor */
{
 if (! waiting)
    XUnmapWindow(XtDisplay(toplevel), WaitCursorWindow);
 else {
      XMapWindow(XtDisplay(toplevel), WaitCursorWindow);
    Waiting = TRUE;		/* to prevent overwrite of important info */
   }

 XFlush(XtDisplay(toplevel));
} /* SetCursor */


/*
** @(#)SetXY() - Set relative window coordinates including specified offset
*/
void
SetXY(Widget target, Widget reference, int X_offset, int Y_offset)
{
 Arg		args[2];
 Display	*dpy = XtDisplay(reference);
 
 Window		dumy;
 int		x, y;


 XTranslateCoordinates(dpy, XtWindow(reference),
                       RootWindow(dpy, DefaultScreen(dpy)),
                       X_offset, Y_offset, &x, &y, &dumy);
 /*
 ** Keep window within root window borders (don't place it off-screen)
 */
 if (! XtIsRealized(target))
    XtRealizeWidget(target);		/* to get width and height values */

 if (x + (int) target->core.width > RootWidth)
    x = RootWidth - target->core.width - 2;

 if (y + (int)target->core.height > RootHeight)
    y = RootHeight - target->core.height - 2;

 XtSetArg(args[0], XtNx, x);
 XtSetArg(args[1], XtNy, y);
 XtSetValues(target, args, 2);
} /* end - SetXY */


/*
** @(#)TextGetLastPos() - return position of last text character
*/
XawTextPosition
TextGetLastPos(Widget w)
{
 TextWidget ctx = (TextWidget) w;
 if (ctx->text.source)
	 return (XawTextSourceScan(ctx->text.source,0,XawstAll,XawsdRight,1,TRUE));
 else
 	 return( (XawTextPosition) 0);
}


/*
** @(#)UpdateTitleBar() - replace information in the title bar title
*/
void
UpdateTitleBar(char *msg)
{
 char		message[BUFSIZ];
 Arg		args[1];
 Widget		w;


 (void) sprintf(message, "%s%d - %s", TITLE, PATCHLEVEL, msg);

 w = XtNameToWidget(toplevel, "topBox.titleBar.title");

 XtSetArg(args[0], XtNlabel, (XtArgVal) message);
 XtSetValues(w, args, 1);
 
 w = XtNameToWidget(toplevel, "topBox.commandPanel.Newmail.Newmail_menu.menu.inc");
 if (w)
    XtSetSensitive(w, In_System_Folder());
} /* UpdateTitleBar */


/*
** @(#)writeTo() - write data to the specified text widget
*/
void
writeTo(Widget w, char *data, int do_append)
{
 XawTextBlock		text;
 XawTextPosition	startPos, endPos;
 int			l, n;


 endPos         = TextGetLastPos(w) + (do_append ? 0 : 1);
 startPos       = (do_append ? endPos : 0);
 text.firstPos  = 0;
 text.length    = (int)strlen(data);
 text.ptr       = data;
 text.format    = FMT8BIT;

 XawTextReplace(w, (XawTextPosition) startPos, (XawTextPosition) endPos, &text);

 XawTextSetInsertionPoint(w, (XawTextPosition) TextGetLastPos(w));

 XawTextInvalidate(w, (XawTextPosition) 0, (XawTextPosition) endPos);
} /* writeTo */


/*
** @(#)writeText() - replace the current text string in the text window.
**                 Also look for an X-Face: header and if found, display.
*/
void
writeText(char *buf)
{
 Arg            args[2];
 Widget		w = XtNameToWidget(toplevel, "topBox.textWindow.text");
#ifdef X_FACE
 Pixmap		bits;
 Dimension	rWidth;
 Display        *dpy = XtDisplay(w);
 Widget         fw;
 Window         rw;
 char           cb[1024], fb[2048], *ptr, *xface;
 int            i, n, x, y;
#endif

 if (buf && *buf) {
#ifndef X_FACE
    XtSetArg(args[0], XtNstring, buf);
    XtSetValues(w, args, 1);
#else
    if (! (fw = XtNameToWidget(XtParent(w), "face"))) {
       XtSetArg(args[0], XtNstring, buf);
       XtSetValues(w, args, 1);
       return;
      }
    /*
    ** First, unmap any current picture.
    **
    ** Look for a line containing an 'X-Face:' header, followed by 72
    ** characters of compressed data.  The second and any subsequent lines
    ** will contain an initial space (which is ignored), followed by 79
    ** characters of compressed data.  The last line may contain fewer than 79
    ** characters.
    **
    ** The X-Face: header and any immediate whitespace (tabs or spaces) will be
    ** removed, and the remaining line placed in the internal buffer (minus
    ** any trailing newline).  On subsequent lines, initial whitespace will be
    ** removed, and the remainder of the data appended to the buffer (minus any
    ** trailing newline).
    **
    ** A blank line, a line without an initial whitespace character,or the
    ** end of the input buffer will signify the end of the X-Face data.  That
    ** buffer will then be uncompressed, and if the data was valid, displayed.
    */

    if (XtIsManaged(fw))
       XtUnmanageChild(fw);

    for (ptr = buf; *ptr; ptr++) {
        if (*ptr == '\n'                    ||
           strncmp(ptr, "Status:", 7) == 0  ||
           strncmp(ptr, "X-Face:", 7) == 0) break;

        for (; *ptr && *ptr != '\n'; ptr++);
       }

    if (! *ptr || strncmp(ptr, "X-Face:", 7) != 0) {
       XtSetArg(args[0], XtNstring, buf);
       XtSetValues(w, args, 1);
       return;
      }

    xface = ptr;	/* keep track of the start position of X-Face header */

    bzero(fb, 2048);
    for (i = 0, ptr += 7; *ptr; ptr++) {
        if (*ptr != ' ' && *ptr != '\t' && *ptr != '\n')
           fb[i++] = *ptr;
        if (*ptr == '\n' && *(ptr+1) && *(ptr+1) != ' ' && *(ptr+1) != '\t')
           break;
       }

    if (XMail.No_X_Hdr) {
       if (*ptr) ptr++;
       bcopy(ptr, xface, (int)strlen(ptr) + 1);	/* suppress the X-Face header */
      }

    XtSetArg(args[0], XtNstring, buf);
    XtSetValues(w, args, 1);

    if (uncompface(fb) >= 0) {
       bzero(cb, 1024);
       for (i = n = 0;i < 1024;) {
           if (! sscanf(&fb[n], "%i%n", &x, &y)) break;
           cb[i++] = revtable[(x >> 8) & 0xFF];
           cb[i++] = revtable[x & 0xFF];
           n += y;
           while (fb[n] && (fb[n]==',' || fb[n]=='\n')) n++;
          }

       XtSetArg(args[0], XtNwidth, &rWidth);
       XtGetValues(XtParent(w), args, 1);

       if ( (int)rWidth > 0 ) 
	    n = (int) rWidth - 52; /* the width of the face minus 4 pixel inset */
       else
	    n = 0;

       XtSetArg(args[0], XtNbitmap, &bits);
       XtGetValues(fw, args, 1);
       if (bits != None)
          XFreePixmap(dpy, bits );

       rw = RootWindow(dpy, DefaultScreen(dpy));
       bits = XCreateBitmapFromData(dpy, rw, cb, 48, 48);
       XtSetArg(args[0], XtNbitmap, bits );
       XtSetArg(args[1], XtNhorizDistance, n);
       XtSetValues(fw, args, 2);

       XtManageChild(fw);
      }
#endif
   }
} /* writeText */
