/* a status bar at bottom on window */

#include <stdarg.h>
#include <stdio.h>  /* STUPID ALPHA/OSF - GCC !!!!! */
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/keysym.h>

#include "HTUtils.h"	/* WWW general purpose macros */
#include "tcp.h"
#include "HTList.h"
#include "HTAccess.h"

#include "www.h"

#define LEFTMARGIN    50
#define RIGHTMARGIN   7
#define status_width    (win_width - ICON_WIDTH)

extern int debug;
extern int initialised;   /* if false it is unsafe to do X output */
extern int busy;
extern Display *display;
extern int screen;
extern int sbar_width;
extern XFontStruct *pFontInfo;
extern unsigned long textColor, labelColor, windowColor, strikeColor,
                     windowTopShadow, windowBottomShadow, statusColor;

#ifdef SELECTION
#include <X11/Xatom.h>
extern unsigned long statusSelectColor; /* howcome 1/9/94 */
#endif

extern unsigned int win_width, win_height;
extern char *user;
char *gatewayUser; /* howcome 20/9/94: removed extern since tcp.c no longer is loaded */
extern int ToolBarHeight;
extern Doc *CurrentDoc; /*, NewDoc; */

#define STATSIZ 256
#define ActiveTextColor labelColor

extern Window win;
GC status_gc;
int statusHeight;
char status[STATSIZ];
char authreq[STATSIZ];
int AbortFlag;
int AbortButton = 1;
int AbortButtonChanged = 0;
int ABup = 1;
int OpenURL;
int FindStr;
int IsIndex;
int SaveFile;
int Authorize;
int statusOffset;
int cursor; /* position of cursor */

#ifdef SELECTION
int startSelect, stopSelect, beginChr, endChr, textWidth=1;
Bool selecting = False;
#endif

/* save strings for specific funcions */

char *OpenString;
char *SaveAsString;
char *FindStrVal;
char *SearchStrVal;
char *FindNextStr;

static int charheight;
static XFontStruct *pStatusFontInfo;
static Button abButton;

void SetStatusWin(Window aWin)
{
    win = aWin;
}

void SetStatusGC(GC aGC)
{
    status_gc = aGC;
}

void SetStatusFont(XFontStruct *pf)
{
    pStatusFontInfo = pf;
    charheight = pf->max_bounds.ascent + pf->max_bounds.descent;
    statusHeight = (Authorize ? 16 + (charheight<<1) : 14 + charheight);
    abButton.label = "Abort";
}

char *LabelString(int *len)
{
    if (OpenURL)
    {
        *len = 5;
        return "Open:";
    }

    if (SaveFile)
    {
        *len = 7;
        return "SaveAs:";
    }

    if (FindStr)
    {
        *len = 5;
        return "Find:";
    }

    if (IsIndex)
    {
        *len = 6;
        return "Match:";
    }

    return NULL;
}

int StatusActive(void)
{
    int c;

    return (Authorize || LabelString(&c));
}


void RestoreStatusString(void)
{
    if (OpenURL)
    {
        if (OpenString)
            strcpy(status, OpenString);
        else
            *status = '\0';
    }
    else if (SaveFile)
    {
        if (SaveAsString)
            strcpy(status, SaveAsString);
        else
            *status = '\0';
    }
    else if (FindStr)
    {
        if (FindStrVal)
            strcpy(status, FindStrVal);
        else
            *status = '\0';
    }
    else if (IsIndex)
    {
        if (SearchStrVal)
            strcpy(status, SearchStrVal);
        else
            *status = '\0';
    }

#ifdef SELCTION
    beginChr = 0;
    endChr = 0;
#endif

    cursor = strlen(status);
}

void SaveStatusString(void)
{
    if (OpenURL)
    {
        if (OpenString)
            Free(OpenString);

        OpenString = strdup(status);
    }
    else if (SaveFile)
    {
        if (SaveAsString)
            Free(SaveAsString);

        SaveAsString = strdup(status);
    }
    else if (FindStr)
    {
        if (FindStrVal)
            Free(FindStrVal);

        FindStrVal = strdup(status);
    }
    else if (IsIndex)
    {
        if (SearchStrVal)
            Free(SearchStrVal);

        SearchStrVal = strdup(status);
    }
}
/* functions DrawOutSet amd DrawInSet changed by Janet for more general use (added Window argument)*/
void DrawOutSet(Window aWin,GC gc, int x, int y, unsigned int w, unsigned int h)
{
    XSetForeground(display, gc, windowTopShadow);

    XFillRectangle(display, aWin, gc, x, y, w, 1);
    XFillRectangle(display, aWin, gc, x, y+1, w-1, 1);
    XFillRectangle(display, aWin, gc, x, y, 1, h);
    XFillRectangle(display, aWin, gc, x+1, y+1, 1, h-1);

    XSetForeground(display, gc, windowBottomShadow);

    XFillRectangle(display, aWin, gc, x, y+h-1, w, 1);
    XFillRectangle(display, aWin, gc, x+1, y+h-2, w-1, 1);
    XFillRectangle(display, aWin, gc, x+w-1, y, 1, h);
    XFillRectangle(display, aWin, gc, x+w-2, y+1, 1, h-1);
}


void DrawInSet(Window aWin, GC gc, int x, int y, unsigned int w, unsigned int h)
{
    XSetForeground(display, gc, windowBottomShadow);

    XFillRectangle(display, aWin, gc, x, y, w, 1);
    XFillRectangle(display, aWin, gc, x, y+1, w-1, 1);
    XFillRectangle(display, aWin, gc, x, y, 1, h);
    XFillRectangle(display, aWin, gc, x+1, y+1, 1, h-1);

    XSetForeground(display, gc, windowTopShadow);

    XFillRectangle(display, aWin, gc, x, y+h-1, w, 1);
    XFillRectangle(display, aWin, gc, x+1, y+h-2, w-1, 1);
    XFillRectangle(display, aWin, gc, x+w-1, y, 1, h);
    XFillRectangle(display, aWin, gc, x+w-2, y+1, 1, h-1);
}

void DrawOutSetCircle(GC gc, int x, int y, unsigned int w, unsigned int h)
{
    XSetForeground(display, gc, windowTopShadow);
    XDrawArc(display, win, gc, x, y, w, h, 45<<6, 180<<6);
    XDrawArc(display, win, gc, x+1, y+1, w-2, h-2, 45<<6, 180<<6);

    XSetForeground(display, gc, windowBottomShadow);
    XDrawArc(display, win, gc, x, y, w, h, 225<<6, 180<<6);
    XDrawArc(display, win, gc, x+1, y+1, w-2, h-2, 225<<6, 180<<6);
}

void DrawInSetCircle(GC gc, int x, int y, unsigned int w, unsigned int h)
{
    XSetForeground(display, gc, windowBottomShadow);
    XDrawArc(display, win, gc, x, y, w, h, 45<<6, 180<<6);
    XDrawArc(display, win, gc, x+1, y+1, w-2, h-2, 45<<6, 180<<6);

    XSetForeground(display, gc, windowTopShadow);
    XDrawArc(display, win, gc, x, y, w, h, 225<<6, 180<<6);
    XDrawArc(display, win, gc, x+1, y+1, w-2, h-2, 225<<6, 180<<6);
}

void DisplayStatusBar()
{
    int x, y, r, n, active;
    unsigned int w, h;
    char *p, *s=NULL;
    XRectangle rect;

    active = ((Authorize || (s = LabelString(&n))) ? 1 : 0);
    statusHeight = (Authorize ? 16 + (charheight<<1) : 14 + charheight);

    rect.x = x = 0;
    rect.y = y = StatusTop; 

    rect.width = w = status_width;
    rect.height = h = statusHeight;

    XSetClipRectangles(display, status_gc, 0, 0, &rect, 1, Unsorted);

    XSetForeground(display, status_gc, windowColor);
    XFillRectangle(display, win, status_gc, x, y, w, h);

    DrawOutSet(win, status_gc, x, y, w, h);

    if (Authorize)
    {
        XSetForeground(display, status_gc, labelColor);

        rect.width -= 2;
        XSetClipRectangles(display, status_gc, 0, 0, &rect, 1, Unsorted);
        XDrawString(display, win, status_gc,
                18, y + 3 + pStatusFontInfo->max_bounds.ascent,
                authreq, strlen(authreq));
    }

    /* howcome 24/2/95: by using a fixed LEFTMARGIN, we're unable to
       scale the abort button horisontally */


    x += LEFTMARGIN;
    y = StatusTop + StatusFontOffset - 2;
    h = 6 + charheight;
    w -= LEFTMARGIN + RIGHTMARGIN;

    XSetForeground(display, status_gc, (active ? statusColor : windowColor));
    XFillRectangle(display, win, status_gc, x, y, w, h);

    DrawInSet(win, status_gc, x, y, w, h);

    /* needs a call to set clipping rectangle */
    /* would currently interact with scrollbar gc */

    rect.x = x + 2;
    rect.y = y;
    rect.width = w - 4;
    rect.height = h;

    r = XTextWidth(pStatusFontInfo, "Abort", 5) + 8;

    abButton.x = 6;
    abButton.y = y + 2;
    abButton.w = r;
    abButton.h = h-3;

    if (AbortButton)
    {
        if (ABup)
            DrawOutSet(win, status_gc, 6, y+2, r, h-3);
        else
            DrawInSet(win, status_gc, 6, y+2, r, h-3);

        XSetForeground(display, status_gc, labelColor);

        XDrawString(display, win, status_gc,
                10, y + 3 + pStatusFontInfo->max_bounds.ascent,
                "Abort", 5);
    }
    else if (s)
    {
        XSetForeground(display, status_gc, labelColor);

        XDrawString(display, win, status_gc,
            8, y + 2 + pStatusFontInfo->max_bounds.ascent, s, n);
    }
    else
        XSetForeground(display, status_gc, labelColor);

    if (Authorize && (p = strchr(status, ':')))
        n = p - status + 1;
    else
        n = strlen(status);

    XSetForeground(display, status_gc, (active ? ActiveTextColor : labelColor));
    XSetClipRectangles(display, status_gc, 0, 0, &rect, 1, Unsorted);
    XDrawString(display, win, status_gc,
        x + 4 - statusOffset, y + 2 + pStatusFontInfo->max_bounds.ascent,
        status, n);

    r = XTextWidth(pStatusFontInfo, status, cursor);

    if (s || Authorize)   /* draw cursor */
    {
        XSetForeground(display, status_gc, strikeColor);
        XFillRectangle(display, win, status_gc, x+3+r - statusOffset, y+2, 1, h-4);
    }

#ifdef SELECTION

    /* howcome 2/8/94: added code to draw highlighted strings. 
       b (=beginChr) and e (=endChr) contains the indexes to start 
       and stop of highlighted chars in status string 
       */


    if (beginChr > 0) {
      XSetForeground(display, status_gc, labelColor);
      XDrawString(display, win, status_gc,
		  (x + 4 - statusOffset), y + 2 + pStatusFontInfo->max_bounds.ascent,
		  status,
		  min(n,beginChr));
    }

    if (beginChr < n) {
      XSetForeground(display, status_gc, strikeColor);
      XDrawString(display, win, status_gc,
		  (x + 4 - statusOffset) + (beginChr * textWidth), y + 2 + pStatusFontInfo->max_bounds.ascent,
		  (status + min(n,beginChr)),
		  (min(n,endChr) - min(n,beginChr)));
    }

    if (endChr < n) {
      XSetForeground(display, status_gc, labelColor);
      XDrawString(display, win, status_gc,
		  (x + 4 - statusOffset) + (endChr * textWidth), y + 2 + pStatusFontInfo->max_bounds.ascent,
		  (status + min(n,endChr)),
		  (n - min(n,endChr)));
    }

    r = XTextWidth(pStatusFontInfo, status, n);

    if (s || Authorize)   /* draw cursor */
    {
        XSetForeground(display, status_gc, strikeColor);
        XFillRectangle(display, win, status_gc, x+3+r - statusOffset, y+2, 1, h-4);
    }

#endif /* SELECTION */

}


#ifdef SELECTION
/* howcome added SelectStatus 1/8/94 */

void SelectStatus(int x_in, int y_in)
{
    int x, y; /* r, n, ch, active;*/ /* janet 21/07/95: not used */
    /*    unsigned int w, h; */	/* janet 21/07/95: not used */
    /*    char *p, *s; */	/* janet 21/07/95: not used */
    /*    XRectangle rect; */ 	/* janet 21/07/95: not used */ 

    if (selecting) {
      x = 50;
      y = win_height - 10 - charheight;
      stopSelect = (x_in - x) / textWidth;
      stopSelect = max(0,stopSelect);
      
      beginChr = min(startSelect,stopSelect);
      endChr = max(startSelect,stopSelect);
      
      DisplayStatusBar();
    }
}

#endif /* SELECTION */

void SetStatusString(char *s)
{
    char *p;
    int x, y, r, n, ch;
    unsigned int w, h;
    XRectangle rect;

    if (initialised)
    {
        if (s)
        {
            strncpy(status, s, STATSIZ-1);
            status[STATSIZ-1] = '\0';

            /* trim trailing \r\n */

            n = strlen(status) - 1;

            if (n > 0 && status[n] == '\n')
            {
                status[n--] = '\0';

                if (status[n] == '\r')
                     status[n] = '\0';
            }
        }

        ch = pStatusFontInfo->max_bounds.ascent + pStatusFontInfo->max_bounds.descent;
        statusHeight = (Authorize ? 16 + (charheight<<1) : 14 + ch);

        rect.x = x = 0;
        rect.y = StatusTop; 

        rect.width = w = status_width;
        rect.height = statusHeight;

        XSetClipRectangles(display, status_gc, 0, 0, &rect, 1, Unsorted);
        XSetForeground(display, status_gc, windowColor);

        h = 14 + charheight;
        y = rect.y + rect.height - h;

        s = LabelString(&r);

        if (AbortButton)
        {
            if (AbortButtonChanged)
            {
                XFillRectangle(display, win, status_gc, 6, y+2, (LEFTMARGIN - 8), h-4);

                if (ABup)
                    DrawOutSet(win, status_gc, abButton.x, abButton.y, abButton.w, abButton.h);
                else
                    DrawInSet(win, status_gc, abButton.x, abButton.y, abButton.w, abButton.h);

                XSetForeground(display, status_gc, labelColor);

                XDrawString(display, win, status_gc,
                        abButton.x + 4, abButton.y + 1 + pStatusFontInfo->max_bounds.ascent,
                        "Abort", 5);

                AbortButtonChanged = 0;  /* avoid flickering buffer */
            }
        }
        else if (s)
        {
            XFillRectangle(display, win, status_gc, 6, y+2, (LEFTMARGIN - 8), h-4);
            XSetForeground(display, status_gc, labelColor);

            XDrawString(display, win, status_gc,
                8, y + 6 + pStatusFontInfo->max_bounds.ascent, s, r);
        }
        else
            XFillRectangle(display, win, status_gc, 6, y+2, (LEFTMARGIN - 8), h-4);

        rect.x = x = LEFTMARGIN + 2;
        rect.y = y = StatusTop + StatusFontOffset; 

        rect.width = w = status_width - LEFTMARGIN - RIGHTMARGIN - 4;
        rect.height = h = 2 + charheight;

        XSetClipRectangles(display, status_gc, 0, 0, &rect, 1, Unsorted);

        XSetForeground(display, status_gc, ((s||Authorize) ? statusColor : windowColor) );
        XFillRectangle(display, win, status_gc, x, y, w, h);

        cursor = n = strlen(status);

        if (Authorize && (p = strchr(status, ':')))
            n = p - status + 1;

        XSetForeground(display, status_gc, ((s || Authorize) ? ActiveTextColor : labelColor));
        XDrawString(display, win, status_gc,
            x + 2 - statusOffset, y + pStatusFontInfo->max_bounds.ascent,
            status, n);

        r = XTextWidth(pStatusFontInfo, status, cursor);

        if (s||Authorize)   /* draw cursor */
        {
            XSetForeground(display, status_gc, strikeColor);
            XFillRectangle(display, win, status_gc, x+1+r - statusOffset, y, 1, h);
        }
    }
    else if (s)
    {
        strncpy(status, s, STATSIZ-1);
        status[STATSIZ-1] = '\0';

        /* trim trailing \r\n */

        n = strlen(status) - 1;

        if (n > 0 && status[n] == '\n')
        {
            status[n--] = '\0';

            if (status[n] == '\r')
                status[n] = '\0';
        }

        cursor = strlen(status);

#ifdef SELECTION
	ClearStatusSelection();
#endif

    }
    XFlush(display); /* howcome 5/10/94: this seems to be required */
}


void RepairStatus(int x1, int moved)
{
    char *p;
    int x, y, r, n, active;
    unsigned int w, h;
    XRectangle rect;

    rect.x = x = LEFTMARGIN + 2;
    rect.y = y = StatusTop + StatusFontOffset; 

    rect.width = w = status_width - LEFTMARGIN - RIGHTMARGIN - 4;
    rect.height = h = 2 + charheight;

    if (!moved)
    {
        if (rect.x < x1)
        {
            rect.width -= x1 - x;
            rect.x = x1;
        }

/*
        r = statusHeight;

        if (r < rect.width)
            rect.width = r;
*/    }

    active = ((Authorize || LabelString(&n)) ? 1 : 0);
    XSetClipRectangles(display, status_gc, 0, 0, &rect, 1, Unsorted);

    XSetForeground(display, status_gc, (active ? statusColor : windowColor));
    XFillRectangle(display, win, status_gc, x, y, w, h);

    if (Authorize && (p = strchr(status, ':')))
        n = p - status + 1;
    else
        n = strlen(status);

    XSetForeground(display, status_gc, (active ? ActiveTextColor : labelColor));
    XDrawString(display, win, status_gc,
        x + 2 - statusOffset, y + pStatusFontInfo->max_bounds.ascent,
        status, n);

    r = XTextWidth(pStatusFontInfo, status, cursor);

    if (Authorize || LabelString(&n))   /* draw cursor */
    {
        XSetForeground(display, status_gc, strikeColor);
        XFillRectangle(display, win, status_gc, x+1+r - statusOffset, y, 1, h);
    }
}

#ifdef SELECTION
void ClearStatusSelection() /* howcome 2/8/94 */
{
    beginChr = 0;
    endChr = 0;
}
#endif

void ClearStatus()
{
    cursor = 0;
    status[0] = '\0';
    statusOffset = 0;
#ifdef SELECTION
    beginChr = 0;
    endChr = 0;
#endif
}

int IsEditChar(char c)
{
    if (c == '\b' || c == '\n' || c == '\r' || c == 21) /* 21 == ^U */
        return 1;

    if (c >= ' ')
        return 1;

    return 0;
}

void EditChar(char c)
{
    char *who;
    int i, n, x1, x2, moved;

    /* x1 is position of cursor, x2 is right edge of clipped text */

    n = strlen(status);

    if (busy && !Authorize)
        Beep();
    else if (c == '\b')
    {
        if (cursor > 0)
        {
            --cursor;
            strcpy(status+cursor, status+cursor+1);
            x1 = LEFTMARGIN + 4 + XTextWidth(pStatusFontInfo, status, cursor);
            x2 = status_width - RIGHTMARGIN - 4;
            n = (x1 > x2 ? x1 - x2 : 0);
            moved = ( (n == statusOffset) ? 0 : 1);
            statusOffset = n;
            RepairStatus(x1 - statusOffset - 1, moved);
        }
        else
            XBell(display, 0);
    }
    else if (c == 21) /* Ctrl-U */
    {
	ClearStatus();
        RepairStatus(statusOffset, 1);
    }
    else if (c == 127)  /* DEL */
    {
        if (cursor < strlen(status))
        {
            strcpy(status+cursor, status+cursor+1);
            x1 = LEFTMARGIN + 4 + XTextWidth(pStatusFontInfo, status, cursor);
            x2 = status_width - RIGHTMARGIN - 4;
            n = (x1 > x2 ? x1 - x2 : 0);
            moved = ( (n == statusOffset) ? 0 : 1);
            statusOffset = n;
            RepairStatus(x1 - statusOffset - 1, moved);
        }
        else
            XBell(display, 0);
    }
    else if ((Authorize || LabelString(&i)) && (c == '\n' || c == '\r') )
    {
        SaveStatusString();

        if (Authorize)
        {
            if (Authorize == GATEWAY)
            {
                if (gatewayUser)
                    Free(gatewayUser);

                gatewayUser = strdup(status);
                Authorize = 0;
                busy = 0;
                status[0] = '\0';
#ifdef SELECTION
		beginChr = endChr = 0;
#endif
                DisplayStatusBar();
                DisplaySizeChanged(0);
                DisplayScrollBar();
                i = 2 + charheight;
                DisplayDoc(WinLeft, WinBottom-i, WinWidth, i);
                XFlush(display);
/*                OpenDoc(NewDoc.url, 0, (strchr(NewDoc.url, ':') ? REMOTE : LOCAL));*/
            }
            else
            {
                who = strdup(status);
                Authorize = 0;
                busy = 0;
                status[0] = '\0';
#ifdef SELECTION
		beginChr = endChr = 0; /* howcome */
#endif
                DisplayStatusBar();
                DisplaySizeChanged(0);
                DisplayScrollBar();
                i = 2 + charheight;
                DisplayDoc(WinLeft, WinBottom-i, WinWidth, i);
                XFlush(display);
/*                OpenDoc(NewDoc.url, who, (strchr(NewDoc.url, ':') ? REMOTE : LOCAL));*/
                Free(who);
            }
        }
        else if (OpenURL) {
	    if (STATUS_TRACE)
		fprintf(stderr,"EditChar: calling OpenDoc(%s, NULL, %s)\n",status, (strchr(status, ':') ? "REMOTE" : "LOCAL"));
#ifdef __QNX__
	    /* assume http:// if no protocol specified */
	    if (strchr(status, ':') == 0) {
		memmove(status + sizeof "http://" - 1, status, strlen(status) + 1);
		memcpy(status, "http://", sizeof "http://" - 1);
	    }
#endif
            OpenDoc(status);
	}
        else if (SaveFile)
            SaveDoc(status);
        else if (FindStr)
        {
            FindNextStr = 0;
            FindString(status, &FindNextStr);
        }
        else if (IsIndex)
            SearchIndex(status);
    }
    else if (c >= ' ')
    {
        if (n < STATSIZ-1)
        {
            for (i = n; i >= cursor; --i)
                status[i+1] = status[i];

            status[cursor++] = c;
            
            x1 = LEFTMARGIN + 3 + XTextWidth(pStatusFontInfo, status, cursor);
            x2 = status_width - RIGHTMARGIN - 4;
            n = (x1 > x2 ? x1 - x2 : 0);
            moved = ( (n == statusOffset) ? 0 : 1);
            statusOffset = n;
            RepairStatus(x1 - statusOffset - XTextWidth(pStatusFontInfo, &c, 1), moved);
        }
        else
            XBell(display, 0);
    }
}

void MoveStatusCursor(int key)
{
    int was, x, y, x1, x2, moved = 0, r, n;
    unsigned int w, h;
    XRectangle rect;

    was = cursor;

    if (key == XK_Left)
    {
        if (cursor > 0)
        {
            --cursor;
            x1 = LEFTMARGIN + 4 + XTextWidth(pStatusFontInfo, status, cursor);
            x2 = status_width - RIGHTMARGIN - 4;
            n = (x1 > x2 ? x1 - x2 : 0);
            moved = ( (n == statusOffset) ? 0 : 1);
            statusOffset = n;

            if (moved)
                RepairStatus(x1 - statusOffset - 1, moved);
        }
        else
            XBell(display, 0);
    }
    else if (key == XK_Right)
    {
        if (cursor < strlen(status))
        {
            ++cursor;
            x1 = LEFTMARGIN + 3 + XTextWidth(pStatusFontInfo, status, cursor);
            x2 = status_width - RIGHTMARGIN - 4;
            n = (x1 > x2 ? x1 - x2 : 0);
            moved = ( (n == statusOffset) ? 0 : 1);
            statusOffset = n;

            if (moved)
                RepairStatus(x1 - statusOffset - XTextWidth(pStatusFontInfo, status+cursor-1, 1), moved);
        }
        else
            XBell(display, 0);
    }

    if (!moved && was != cursor)
    {
        rect.x = x = LEFTMARGIN + 2;
        rect.y = y = StatusTop + StatusFontOffset; 

        rect.width = w = status_width - (LEFTMARGIN + RIGHTMARGIN + 4);
        rect.height = h = 2 + charheight;

        XSetClipRectangles(display, status_gc, 0, 0, &rect, 1, Unsorted);

        XSetForeground(display, status_gc, statusColor);
        r = XTextWidth(pStatusFontInfo, status, was);
        XFillRectangle(display, win, status_gc, x+1+r - statusOffset, y, 1, h);

        XSetForeground(display, status_gc, strikeColor);
        r = XTextWidth(pStatusFontInfo, status, cursor);
        XFillRectangle(display, win, status_gc, x+1+r - statusOffset, y, 1, h);
    }
}

void Announce(char *args, ...)
{
    va_list ap;
    char buf[512];

    if (initialised)
    {
        va_start(ap, args);
        vsprintf(buf, args, ap);
        va_end(ap);

        SetStatusString(buf);
        XFlush(display);
    }
    else
    {
        va_start(ap, args);
        vsprintf(buf, args, ap);
        va_end(ap);

        SetStatusString(buf);
    }
    /* howcome 16/10/94: moved the following statments outside block */
    if (STATUS_TRACE)
	fprintf(stderr, "%s\n", buf);
}

void Warn(char *args, ...)
{
    va_list ap;
    char buf[512];

    if (initialised)
    {
        va_start(ap, args);
        vsprintf(buf, args, ap);
        va_end(ap);

        if (STATUS_TRACE)
            fprintf(stderr, "%s\n", buf);

        SetStatusString(buf);
        XBell(display, 0);
        XFlush(display);
    }
    else
    {
        va_start(ap, args);
        vsprintf(buf, args, ap);
        va_end(ap);

        SetStatusString(buf);
        fprintf(stderr, "%s\n", buf);
    }
}

void Beep()
{
    XBell(display, 0);
    XFlush(display);
}

int StatusButtonDown(int button, int px, int py)
{
    char *s;
    int x, y, n;
    unsigned int w, h;
    XRectangle rect;

    x = abButton.x;
    y = abButton.y;
    w = abButton.w;
    h = abButton.h;

    rect.x = x;
    rect.y = y;
    rect.width = w;
    rect.height = h;


#ifdef SELECTION
    /* howcome 1/8/94 */
    if (button == Button1 && !OpenURL /* && !AbortButton */)
    {
      textWidth = XTextWidth(pStatusFontInfo, " ", 1);
      if (textWidth > 0) {
	startSelect = (px - 50 - 4) / textWidth;	/* 50 should be a define */
/*	startSelect = min(startSelect,strlen(status));*/
	if (startSelect >= 0) { 
	  beginChr = endChr = startSelect;
	  selecting = True;
	}
      }
/*      fprintf(stderr,"%d down x %d y %d px %d py %d w %d h %d\n",startSelect,x,y,px,py,w,h);*/
    }

#endif


    if (button == Button1 && AbortButton &&
        x <= px &&
        px < x + w &&
        y <= py &&
        py < y + h)
    {
        ABup = 0;
        XSetClipRectangles(display, status_gc, 0, 0, &rect, 1, Unsorted);
        DrawInSet(win, status_gc, x, y, w, h);
        return STATUS;
    }

    /* now check for middle or right button down over status window */

    x = LEFTMARGIN + 2;
    y = StatusTop + StatusFontOffset; 

    w = status_width - (LEFTMARGIN + RIGHTMARGIN + 4);
    h = statusHeight - 12;

    if ((button == Button2 || button == Button3) && LabelString(&n) &&
        x <= px &&
        px < x + w &&
        y <= py &&
        py < y + h)
    {
        s = XFetchBytes(display, &n);
	s = strtok(s,"\n");		/* howcome 24/5/95: remove newlines */
	n = strlen(s);

        if (s)
        {
            x = strlen(status);
            if (x + n > STATSIZ-1)
                n = STATSIZ-1-x;

            strncpy(status+x, s, n);
            status[x+n] = '\0';
            cursor = x+n;
 
            XFree(s);
            Redraw(x, y, w, h);
        }
    }
#ifdef SELECTION
    return STATUS; /* howcome 1/8/94: indicate interest in buttonup since user may select (part of) url */
#else
    return VOID;
#endif
}

void HideAuthorizeWidget(void)
{
    AbortButton = 0;
    Authorize = 0;
    busy = 0;
    ClearStatus();
    DisplayStatusBar();
    DisplaySizeChanged(0);
    DisplayScrollBar();
    DisplayDoc(WinLeft, WinTop, WinWidth, WinHeight);
    XFlush(display);
}

void StatusButtonUp(int px, int py)
{
    int x, y;
    unsigned int w, h;
    XRectangle rect;

    x = abButton.x;
    y = abButton.y;
    w = abButton.w;
    h = abButton.h;

    rect.x = x;
    rect.y = y;
    rect.width = w;
    rect.height = h;

    if (AbortButton)
    {
        /* redraw button in up state */

        if (ABup == 0)
        {
            ABup = 1;
            XSetClipRectangles(display, status_gc, 0, 0, &rect, 1, Unsorted);
            DrawOutSet(win, status_gc, x, y, w, h);
        }

        /* check if up event occurs in button */

        if (x <= px &&
            px < x + w &&
            y <= py &&
            py < y + h)
        {
            AbortFlag = 1;

            if (Authorize)  /* hide password widget */
                HideAuthorizeWidget();
        }
    }

#ifdef SELECTION
    if (selecting) {	/* howcome 1/8/94 */
      char *s;
      const int n = strlen(status);
      
      if (beginChr > n && endChr > n) {		/* off bounds, select everything */
	beginChr = 0;
	endChr = strlen(status);
      }

      if (beginChr == endChr && beginChr < n) {
	char *pb, *pe;

	pb = pe = status + beginChr;			/* search for sensible strings */
	while(pb >= status && *pb != ':' && *pb !='/')
	  pb--;
	pb++;
	while(*pe != '\0' && *pe != ':' && *pe !='/')
	  pe++;
	  
	beginChr = pb - status;
	endChr = pe - status;
      }

      if (beginChr > endChr) {		/* this happens to be true when a ':' of '/' has been pressed, select everything */
	beginChr = 0;
	endChr = n;
      }

      endChr = min(n, endChr); /* howcome 8/3/95 */

      s = strdup(status);
      strncpy(s, status + beginChr, endChr - beginChr);
      s[endChr-beginChr] = '\0';

      if (STATUS_TRACE) 
	fprintf(stderr,"Set selection: \"%s\"\n",s);
      DisplayStatusBar();

      SetSelection(s);
      Free(s);
      selecting = False;
    }

#endif /* SELECTION */

}

void ShowAbortButton(int n)
{
    AbortFlag = 0;
    AbortButton = n;
    AbortButtonChanged = 1;
}

/* reconfigure status bar to ask for authorisation
   mode is REMOTE for remote hosts, and GATEWAY for the gateway */

void GetAuthorization(int mode, char *host)
{
    Authorize = mode;
    busy = 1;
    ClearStatus();

    ShowAbortButton(1);

    if (mode == GATEWAY)
    {
        strcpy(authreq, "Enter name:password for gateway ");
        strncpy(authreq+32, host, STATSIZ-33);
    }
    else
    {
        strcpy(authreq, "Enter name:password for ");
        strncpy(authreq+24, host, STATSIZ-25);
    }


    authreq[STATSIZ-1] = '\0';

    if (user)
        sprintf(status, "%s:", user);
    else {
        status[0] = '\0';
#ifdef SELCTION
	beginChr = endChr = 0; /* howcome */
#endif
    }
    cursor = strlen(status);

    if (initialised)
    {
        DisplayStatusBar();
        DisplaySizeChanged(0);
        DisplayScrollBar();
    }
    else
        DisplaySizeChanged(0);
}

/* extract name from "name:password" */

char *UserName(char *who)
{
    char *p;
    static char name[32];

    strncpy(name, who, 30);
    name[31] = '\0';
    p = strchr(name, ':');

    if (p)
        *p = '\0';
    return name;
}

/* extract password from "name:password" */

char *PassStr(char *who)
{
    char *p;

    p = strchr(who, ':');

    return (p ? p+1 : "");
}
