/*********************************************************************** * * TITLE: * message.c * * AUTHOR: * Kevin J. Miller, Ana Maria Guerrero * * DESCRIPTION: * This module is a generic OEL module that will create and * support either a scrolling message area or a popup message * box. * * The programmer's interface to the message area are by the * functions Message and Warning which have the same interface * as printf. * * The scrolling message area supports the following subresources: * * *logText.text.font: a fixed width font * *logText.text.lines: number of lines to display * *logText.text.save: number of lines to save * *logText.text.file: optional log file of messages * * CHANGE HISTORY * * $Log: message.c,v $ * Revision 1.24 1996/12/19 22:33:49 kevin * fixed compiler warnings * * Revision 1.23 1995/01/20 04:11:24 kevin * fixed acc warnings * * Revision 1.22 1994/05/03 19:38:33 kevin * fixed arg 3 of strncpy * * Revision 1.21 1994/01/07 22:06:52 kevin * updates after HP test * * Revision 1.20 1994/01/06 05:12:06 kevin * port for V20 * * Revision 1.19 1993/10/26 23:50:16 kevin * added temporary startup message queue * * Revision 1.18 1993/10/18 23:10:14 kevin * use Xt selection instead of Xlib * * Revision 1.17 1993/05/18 22:32:30 kevin * fixed Warning (not reading more than one parameter) * * Revision 1.16 1993/04/15 21:32:41 kevin * fixed comments for resizeCB * * Revision 1.15 1993/04/15 04:47:59 kevin * added resize, cut, log file, and finite save size * alternate dialog output mode * * Revision 1.14 1992/04/23 20:02:49 kevin * removed unused tmp_start and tmp_last * * Revision 1.13 1991/12/19 04:28:04 kevin * implemented new scrolling message area * * Revision 1.12 1991/07/17 02:26:33 kevin * added resource dependent size to message area * * Revision 1.11 1991/06/04 20:35:23 kevin * added 'font' subresources to message and drawing drawingArea * removed 'font' application resource * * Revision 1.10 1991/05/23 01:11:06 kevin * added new message area * * Revision 1.9 1991/05/14 02:17:59 kevin * minor lint fixes * * Revision 1.8 1991/04/15 20:24:07 kevin * corrected memory leakage: freed all temporary strings * * Revision 1.7 1991/04/12 01:11:31 kevin * added standard SFOC header * * Revision 1.6 1991/04/10 22:30:51 kevin * changed name of common include file to soeedt.h * * Revision 1.5 1991/04/09 21:28:31 kevin * fixed warning and added bell * * Revision 1.4 1991/04/08 21:37:54 kevin * used vsprintf in message so you can print fdormatted text * * Revision 1.3 1991/03/11 22:15:42 kevin * blanked out initial message * * Revision 1.2 1991/03/05 22:32:59 kevin * isolated all but form position code to specific widget * * Revision 1.1 1991/03/04 21:44:38 kevin * Initial revision * *********************************************************************** * * WARNINGS: * * EXTERNAL CALLABLE COMPONENTS (PUBLIC): * InitMessage * Message * Warning * * GLOBALS: * none * * WAIVERS: * none * * NOTES: * * MANPAGE: * ***********************************************************************/ #ifndef lint static const char rcsid[] = "$Id: message.c,v 1.24 1996/12/19 22:33:49 kevin OEL $"; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "message.h" #define HORZ_MARGIN 6 #define VERT_MARGIN 2 #define VERT_SPACING 2 #define BUFLEN 1024 /* * structures for adding subresources */ typedef struct { Font font; int lines; String file; int save; } SRData, *SRDataPtr; static XtResource resources[] = { { XtNfont, XtCFont, XtRFont, sizeof(Font), XtOffset(SRDataPtr, font), XtRString, "fixed" }, { "lines", "Lines", XtRInt, sizeof(int), XtOffset(SRDataPtr, lines), XtRString, "6" }, { "file", "File", XtRString, sizeof(String), XtOffset(SRDataPtr, file), XtRString, "" }, { "save", "Save", XtRInt, sizeof(int), XtOffset(SRDataPtr, save), XtRString, "64" } }; /* * temporary queue linked list */ struct tmp_que { char *txtptr; struct tmp_que *nxtptr; }; /* * local variables for both modes */ static SRData data; /* subresource data for message area */ static Display *display; /* pointer to display structure */ static struct tmp_que *queorg; /* origin of temporary queue */ /* * local variables for Popup mode */ static Widget msgPopup; /* dialog widget if dialog is enabled */ static int type; /* type of message (message or warning) */ /* * local variables for scroll area mode */ static Window win; /* drawing window for X */ static GC gc; /* graphics context for window */ static Pixel normalFG; /* normal foreground index */ static Pixel normalBG; /* normal background index */ static Pixel selectFG; /* select foreground index */ static Pixel selectBG; /* select background index */ static Dimension height; /* current height of message area */ static Dimension width; /* current width of message area */ static int columns; /* number of columns in message area */ static int cheight; /* character height in pixels */ static int cwidth; /* character width in pixels */ static int cstart_x; /* x offset of first character in pixels */ static int cstart_y; /* y offset of first character in pixels */ static int cell_x; /* x offset of first cell in pixels */ static int cell_y; /* y offset of first cell in pixels */ static Widget logScroll; /* scrollbar widget */ static FILE *logfp; /* log file pointer */ static char *logbuf; /* internal buffer for log messages */ static int logorg; /* current origin of log (circular queue) */ static int logsize; /* current size of the log */ static int logstart; /* current starting index in display area */ /* * local variables for text selection (scroll area mode only) */ static int sel_active; /* True when select is being done (EH) */ static int sel_start_row; /* row of first selected character */ static int sel_start_col; /* column of first selected character */ static int sel_cur_row; /* row currently under the cursor */ static int sel_cur_col; /* column currently under the cursor */ static Region sel_rgn; /* Region defining the select area */ static char *sel_buf; /* text buffer for selection */ /* * local functions */ static void initialCB(Widget, XtPointer, XmDrawingAreaCallbackStruct *); static void message(char *); static void doMessage(char *); static void addToLog(char *); static char *getLogMessage(int); static void addMessageExpose(void); static void exposeCB(Widget, XtPointer, XmDrawingAreaCallbackStruct *); static void drawText(void); static void logScrollCB(Widget, char *, XmScrollBarCallbackStruct *); static void resizeCB(Widget, XtPointer, XmDrawingAreaCallbackStruct *); static void inputCB(Widget, XtPointer, XmDrawingAreaCallbackStruct *); static void locate(int *, int *, int, int); static void selectEH(Widget, XtPointer, XEvent *); static void loseSelCB(Widget, Atom *); static Boolean convertCB(Widget, Atom *, Atom *, Atom *, char **, unsigned long *, int *); static void setSelect(void); static void copySelect(void); static void appendToRegion(Region, int, int, int, int); /*********************************************************************** * * FUNCTION: * InitMessage * * INPUTS: * parent - (Widget) parent of form * text - (String) title text for popup * * OUTPUTS: * none * * RETURNS: * messageArea - (Widget) form containing message area * * EXTERNALLY READ: * resources - (static ) definition of subresources * * EXTERNALLY MODIFIED: * data - (static SRData) subresource data for message area * display - (static Display *) pointer to display structure * msgPopup - (static Widget) dialog widget if dialog is enabled * type - (static int) type of message (message or warning) * height - (static Dimension) current height of message area * cheight - (static int) character height in pixels * cwidth - (static int) character width in pixels * cstart_x - (static int) x offset of first character in pixels * cstart_y - (static int) y offset of first character in pixels * cell_x - (static int) x offset of first cell in pixels * cell_y - (static int) y offset of first cell in pixels * logScroll - (static Widget) scrollbar widget * logfp - (static FILE *) log file pointer * * DESCRIPTION: * Initializes all X11 variables needed by the message area widget. * If the parent is an Application Shell, a Message Dialog is created * and the text argument is used as the dialog title. Otherwise, * a scrolling message area is created. * * The following is a graphical view of the widget heirarchy for the * scrolling message area. * * parent--+ * | * +--messageArea--+ (XmForm) * | * +--logScroll (XmScrollBar) * | * +--logFrame --+ (XmFrame) * | * +-- logText (XmDrawingArea) * * If the lines saved is equal to the lines displayed, then the * scrollbar is not created. */ Widget InitMessage(Widget parent, ...) { va_list ap; /* variable function argument list */ Cardinal n; /* standard count of Xt arguments */ Arg args[20]; /* standard Xt argument list */ char *text; /* title for Popup */ XmString tmpString; /* title for Popup */ Widget messageArea; Widget logFrame; Widget logText; /* widget for drawing area */ XFontStruct *xf; va_start(ap, parent); display = XtDisplay(parent); if (XtClass(parent) == (WidgetClass)applicationShellWidgetClass) { /* * create a popup message box if the parent is App. Shell */ text = va_arg(ap, char *); tmpString = XmStringCreateSimple(text); type = XmDIALOG_INFORMATION; n = 0; XtSetArg(args[n], XmNdialogTitle, tmpString); n++; XtSetArg(args[n], XmNdialogType, type); n++; msgPopup = XmCreateMessageDialog(parent, "messageBox", args, n); XtUnmanageChild(XmMessageBoxGetChild(msgPopup, XmDIALOG_CANCEL_BUTTON)); XtUnmanageChild(XmMessageBoxGetChild(msgPopup, XmDIALOG_HELP_BUTTON)); XmStringFree(tmpString); messageArea = NULL; } else { msgPopup = NULL; /* * create a scrolling message area otherwise */ n = 0; messageArea = XmCreateForm(parent, "messageArea", args, n); XtManageChild(messageArea); n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; logFrame = XmCreateFrame(messageArea, "logFrame", args, n); XtManageChild(logFrame); n = 0; XtSetArg(args[n], XmNheight, 0); n++; logText = XmCreateDrawingArea(logFrame, "logText", args, n); XtManageChild(logText); XtGetSubresources(logText, &data, "text", "Text", resources, XtNumber(resources), NULL, 0); if (data.save <= data.lines) { /* * do not create a scrollbar */ data.save = data.lines; n = 0; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetValues(logFrame, args, n); } else { /* * set up scrollbar parameters */ n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; /* * MOTIF BUG: for some odd reason, the scrollbar cannot be * resized smaller, so we start it off with a very * small size. */ XtSetArg(args[n], XmNheight, 10); n++; XtSetArg(args[n], XmNminimum, 0); n++; XtSetArg(args[n], XmNmaximum, data.save); n++; XtSetArg(args[n], XmNsliderSize, data.save); n++; XtSetArg(args[n], XmNvalue, 0); n++; XtSetArg(args[n], XmNincrement, 1); n++; XtSetArg(args[n], XmNpageIncrement, data.lines); n++; logScroll = XmCreateScrollBar(messageArea, "logScroll", args, n); XtManageChild(logScroll); XtAddCallback(logScroll, XmNvalueChangedCallback, (XtCallbackProc)logScrollCB, NULL); n = 0; XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNrightWidget, logScroll); n++; XtSetValues(logFrame, args, n); } /* * calculate text parameters */ xf = XQueryFont(display, data.font); cstart_x = HORZ_MARGIN; cstart_y = VERT_MARGIN + xf->ascent; cell_x = HORZ_MARGIN; cell_y = VERT_MARGIN - VERT_SPACING / 2; cheight = xf->ascent + xf->descent + VERT_SPACING; cwidth = xf->max_bounds.width; height = data.lines * cheight + 2 * VERT_MARGIN; XFreeFontInfo(NULL, xf, 1); n = 0; XtSetArg(args[n], XmNheight, height); n++; XtSetValues(logText, args, n); XtAddCallback(logText, XmNexposeCallback, (XtCallbackProc)initialCB, NULL); XtAddCallback(logText, XmNexposeCallback, (XtCallbackProc)exposeCB, NULL); XtAddCallback(logText, XmNresizeCallback, (XtCallbackProc)resizeCB, NULL); XtAddCallback(logText, XmNinputCallback, (XtCallbackProc)inputCB, NULL); /* * open a log file if requested */ if (*(data.file) != '\0') { logfp = fopen(data.file, "w"); } } va_end(ap); return messageArea; } /*********************************************************************** * * FUNCTION: * initialCB * * INPUTS: * w - (Widget) the drawing area * client_data - (XtPointer) not used * call_data - (XmDrawingAreaCallbackStruct) not used * * OUTPUTS: * none * * RETURNS: * none * * EXTERNALLY READ: * data - (static SRData) subresource data for message area * display - (static Display *) pointer to display structure * cwidth - (static int) character width in pixels * * EXTERNALLY MODIFIED: * win - (static Window) drawing window for X * gc - (static GC) graphics context for window * normalFG - (static Pixel) normal foreground index * normalBG - (static Pixel) normal background index * selectFG - (static Pixel) select foreground index * selectBG - (static Pixel) select background index * width - (static Dimension) current width of message area * columns - (static int) number of columns in message area * logbuf - (static char *) internal buffer for log messages * sel_buf - (static char *) text buffer for selection * * DESCRIPTION: * Message area initialization that must occur after the Widgets * are realized. */ static void initialCB(Widget w, XtPointer client_data, XmDrawingAreaCallbackStruct *call_data) { Cardinal n; Arg args[6]; XGCValues gcval; struct tmp_que **quehdl; #if defined(spr) && defined(DEBUG) (void)client_data; /* parameter not used */ (void)call_data; /* parameter not used */ #endif XtRemoveCallback(w, XmNexposeCallback, (XtCallbackProc)initialCB, NULL); n = 0; XtSetArg(args[n], XmNforeground, &normalFG); n++; XtSetArg(args[n], XmNbackground, &normalBG); n++; XtSetArg(args[n], XmNhighlightColor, &selectBG); n++; XtSetArg(args[n], XmNwidth, &width); n++; XtGetValues(w, args, n); if ((selectBG == normalFG) || (selectBG == normalBG)) { selectFG = normalBG; selectBG = normalFG; } else { selectFG = normalFG; } gcval.foreground = normalFG; gcval.background = normalBG; gcval.font = data.font; win = XtWindow(w); gc = XCreateGC(display, win, GCForeground | GCBackground | GCFont, &gcval); columns = ((int)width - 2 * HORZ_MARGIN) / cwidth; logbuf = (char *)XtMalloc((Cardinal)(data.save * (columns + 2))); if (logbuf == NULL) { fprintf(stderr, "Unable to create message buffer:\n"); fprintf(stderr, " lines = %d, columns = %d\n", data.save, columns); } sel_buf = (char *)XtMalloc((Cardinal)(data.lines * (columns + 2))); if (sel_buf == NULL) { fprintf(stderr, "Unable to create selection buffer:\n"); fprintf(stderr, " lines = %d, columns = %d\n", data.lines, columns); } else { *sel_buf = '\0'; } /* * dump out temporary message queue */ while (queorg != NULL) { quehdl = &queorg; while ((*quehdl)->nxtptr != NULL) { quehdl = &((*quehdl)->nxtptr); } message((*quehdl)->txtptr); XtFree((*quehdl)->txtptr); XtFree((char *)(*quehdl)); *quehdl = NULL; } } /*********************************************************************** * * FUNCTION: * Message * * INPUTS: * same as printf * * OUTPUTS: * none * * RETURNS: * none * * EXTERNALLY READ: * none * * EXTERNALLY MODIFIED: * none * * DESCRIPTION: * Formats text and passes a simple string to message(). */ /*VARARGS*/ void Message(char *fmt, ...) { va_list ap; static char buff[BUFLEN]; va_start(ap, fmt); (void)vsprintf(buff, fmt, ap); message(buff); va_end(ap); } /*********************************************************************** * * FUNCTION: * Warning * * INPUTS: * same as printf * * OUTPUTS: * none * * RETURNS: * none * * EXTERNALLY READ: * display - (static Display *) pointer to display structure * * EXTERNALLY MODIFIED: * type - (static int) type of message (message or warning) * * DESCRIPTION: * Formats text and passes a simple string to message(), but adds * a bell and sets the dialog type if a popup is used. */ /*VARARGS*/ void Warning(char *fmt, ...) { va_list ap; static char buff[BUFLEN]; /* * ring bell on terminal */ if (display != NULL) { XBell(display, 100); } type = XmDIALOG_WARNING; va_start(ap, fmt); (void)vsprintf(buff, fmt, ap); message(buff); va_end(ap); type = XmDIALOG_INFORMATION; } /*********************************************************************** * * FUNCTION: * message * * INPUTS: * buff - (char *) string to print * * OUTPUTS: * none * * RETURNS: * none * * EXTERNALLY READ: * msgPopup - (static Widget) dialog widget if dialog is enabled * type - (static int) type of message (message or warning) * logScroll - (static Widget) scrollbar widget * logfp - (static FILE *) log file pointer * * EXTERNALLY MODIFIED: * none * * DESCRIPTION: * Formats message into a simple string before writing to the * output device. Tabs are expanded to eight spaces, and an * implicit newline terminates the text. */ /*VARARGS*/ static void message(char *buff) { static char text[BUFLEN]; int lastc; /* last character in string */ XmString tmpString; Arg args[4]; Cardinal n; int i, j; struct tmp_que *queptr; /* * expand tabs */ for (i = 0, j = 0; buff[i]; ++i) { if (isprint(buff[i]) || (buff[i] == '\n')) { text[j++] = buff[i]; } else if (buff[i] == '\t') { do { text[j++] = ' '; } while (j % 8); } } lastc = j - 1; text[j] = '\0'; if (msgPopup != NULL) { /* * strip s from end of the string */ while ((lastc >= 0) && (text[lastc] == '\n')) { text[lastc--] = '\0'; } tmpString = XmStringCreateLtoR(text, XmSTRING_ISO8859_1); n = 0; XtSetArg(args[n], XmNmessageString, tmpString); n++; XtSetArg(args[n], XmNdialogType, type); n++; XtSetValues(msgPopup, args, n); XmStringFree(tmpString); XtManageChild(msgPopup); } else if (logbuf != NULL) { /* * guarantee at least a at the end of the string */ if ((lastc < 0) || (text[lastc] != '\n')) { text[++lastc] = '\n'; text[++lastc] = '\0'; } doMessage(text); if (logfp != NULL) { fputs(text, logfp); fflush(logfp); } } else if (display != NULL) { /* * save messages in an internal queue */ queptr = XtNew(struct tmp_que); queptr->txtptr = XtNewString(text); queptr->nxtptr = queorg; queorg = queptr; } else { /* * guarantee at least a at the end of the string */ if ((lastc < 0) || (text[lastc] != '\n')) { text[++lastc] = '\n'; text[++lastc] = '\0'; } (void)fputs(text, stderr); } } /*********************************************************************** * * FUNCTION: * doMessage * * INPUTS: * text - (char *) text to display * * OUTPUTS: * none * * RETURNS: * none * * EXTERNALLY READ: * data - (static SRData) subresource data for message area * logScroll - (static Widget) scrollbar widget * logsize - (static int) current size of the log * * EXTERNALLY MODIFIED: * logstart - (static int) current starting index in display area * * DESCRIPTION: * Saves the message in a buffer, and outputs it to the scrolling * text area. */ static void doMessage(char *text) { int new_value; int new_slider; int value; int slider; int incr; int page; /* * add to internal array */ addToLog(text); /* * find new start index (where L = data.lines, S = data.save) * * logsize value slider * * 0 0 S * 1 0 S * L - 1 0 S * L 0 S * L + 1 1 S - 1 * L + 2 2 S - 2 * S - 1 S - L - 1 L - 1 * S S - L L */ if (logsize > data.lines) { new_value = logsize - data.lines; new_slider = data.save - new_value; } else { new_value = 0; new_slider = data.save; } logstart = new_value; addMessageExpose(); if (logScroll != NULL) { /* * fix scroll bar */ XmScrollBarGetValues(logScroll, &value, &slider, &incr, &page); if ((value != new_value) || (slider != new_slider)) { XmScrollBarSetValues(logScroll, new_value, new_slider, incr, page, False); } } } /*********************************************************************** * * FUNCTION: * addToLog * * INPUTS: * text - (char *) a text string * * OUTPUTS: * none * * RETURNS: * none * * EXTERNALLY READ: * data - (static SRData) subresource data for message area * columns - (static int) number of columns in message area * * EXTERNALLY MODIFIED: * logbuf - (static char *) internal buffer for log messages * logorg - (static int) current origin of log (circular queue) * logsize - (static int) current size of the log * * DESCRIPTION: * Adds a text string message to internal storage. Also logsize * is incremented in order to keep a current count of messages. */ static void addToLog(char *text) { char *ptr; /* current text pointer */ char save; /* saved character for broken line */ char *spos; /* local of saved character */ char *out; /* output pointer */ ptr = text; while (*ptr) { /* * define output pointer (wrap at data.save lines) */ if (logsize >= data.save) { out = logbuf + (logorg * (columns + 2)); if (++logorg >= data.save) { logorg = 0; } } else { out = logbuf + (logsize * (columns + 2)); ++logsize; } /* * advance past the first */ spos = strchr(ptr, '\n') + 1; save = *spos; *spos = '\0'; /* * check the string length and adjust if necessary */ if ((int)strlen(ptr) > columns + 1) { *spos = save; spos = ptr + columns; save = *spos; *spos = '\0'; } /* * copy the text */ while ((*(out++) = *(ptr++))); /* * go to the next substring */ *spos = save; ptr = spos; } } /*********************************************************************** * * FUNCTION: * getLogMessage * * INPUTS: * i - (int) line (from 0) of current display * * OUTPUTS: * none * * RETURNS: * msg - (char *) pointer to message string (static) * * EXTERNALLY READ: * columns - (static int) number of columns in message area * logsize - (static int) current size of message array * logorg - (static int) current beginning of array * logbuf - (static char *) message array * * EXTERNALLY MODIFIED: * none * * DESCRIPTION: * Retrieve a log message from the internal list by index. If * the requested index is larger that the current size of the * message array then the empty string is returned. */ static char * getLogMessage(int i) { char *msg; if ((i >= 0) && (i < logsize)) { msg = logbuf + (((i + logorg) % logsize) * (columns + 2)); } else { msg = ""; } return msg; } /*********************************************************************** * * FUNCTION: * addMessageExpose * * INPUTS: * none * * OUTPUTS: * none * * RETURNS: * none * * EXTERNALLY READ: * display - (static Display *) pointer to display structure * win - (static Window) drawing window for X * height - (static Dimension) current height of message area * width - (static Dimension) current width of message area * sel_active - (static int) True when select is being done (EH) * * EXTERNALLY MODIFIED: * sel_rgn - (static Region) Region defining the select area * * DESCRIPTION: * Generate an X11 Expose event in order to update part of the * message area. Currently there is no optimization of update area. */ static void addMessageExpose(void) { XEvent event; if (!sel_active && (sel_rgn != None)) { XDestroyRegion(sel_rgn); sel_rgn = None; } XClearArea(display, win, 0, 0, (unsigned int)width, (unsigned int)height, False); event.type = Expose; event.xexpose.serial = 0; event.xexpose.send_event = False; event.xexpose.display = display; event.xexpose.window = win; event.xexpose.x = 0; event.xexpose.y = 0; event.xexpose.width = width; event.xexpose.height = height; event.xexpose.count = 0; XtDispatchEvent(&event); XFlush(display); } /*********************************************************************** * * FUNCTION: * exposeCB * * INPUTS: * w - (Widget) not used * client_data - (XtPointer) not used * call_data - (XmDrawingAreaCallbackStruct) expose area * * OUTPUTS: * none * * RETURNS: * none * * EXTERNALLY READ: * display - (static Display *) pointer to display structure * win - (static Window) drawing window for X * gc - (static GC) graphics context for window * normalFG - (static Pixel) normal foreground index * selectFG - (static Pixel) select foreground index * selectBG - (static Pixel) select background index * height - (static Dimension) current height of message area * width - (static Dimension) current width of message area * sel_rgn - (static Region) Region defining the select area * * EXTERNALLY MODIFIED: * none * * DESCRIPTION: * Standard expose callback for a drawing area. */ static void exposeCB(Widget w, XtPointer client_data, XmDrawingAreaCallbackStruct *call_data) { XRectangle rectangle; static Region clip_region = None; /* clip redraw area */ Region hl_rgn; #if defined(spr) && defined(DEBUG) (void)w; /* parameter not used */ (void)client_data; /* parameter not used */ #endif rectangle.x = call_data->event->xexpose.x; rectangle.y = call_data->event->xexpose.y; rectangle.width = call_data->event->xexpose.width; rectangle.height = call_data->event->xexpose.height; if (clip_region == None) clip_region = XCreateRegion(); XUnionRectWithRegion(&rectangle, clip_region, clip_region); if (!(call_data->event->xexpose.count)) { XSetRegion(display, gc, clip_region); drawText(); if (sel_rgn != None) { hl_rgn = XCreateRegion(); XIntersectRegion(clip_region, sel_rgn, hl_rgn); XSetRegion(display, gc, hl_rgn); XSetForeground(display, gc, selectBG); XFillRectangle(display, win, gc, 0, 0, (unsigned int)width, (unsigned int)height); XSetForeground(display, gc, selectFG); drawText(); XSetForeground(display, gc, normalFG); XDestroyRegion(hl_rgn); } /* * remove clip mask */ XDestroyRegion(clip_region); clip_region = None; } } /*********************************************************************** * * FUNCTION: * drawText * * INPUTS: * none * * OUTPUTS: * none * * RETURNS: * none * * EXTERNALLY MODIFIED: * data - (static SRData) subresource data for message area * display - (static Display *) pointer to display structure * win - (static Window) drawing window for X * gc - (static GC) graphics context for window * cheight - (static int) character height in pixels * cstart_x - (static int) x offset of first character in pixels * cstart_y - (static int) y offset of first character in pixels * logstart - (static int) current starting index in display area * * EXTERNALLY READ: * none * * DESCRIPTION: * Draw the current text to the screen. */ static void drawText(void) { int i; int len; int row; char *msg; row = cstart_y; for (i = 0; i < data.lines; ++i) { msg = getLogMessage(logstart + i); len = strlen(msg); if (len > 0) { if (*(msg + len - 1) == '\n') --len; XDrawString(display, win, gc, cstart_x, row, msg, len); } row += cheight; } } /*********************************************************************** * * FUNCTION: * logScrollCB * * INPUTS: * w - (Widget) the drawing area * client_data - (XtPointer) not used * call_data - (XmScrollBarCallbackStruct) position * * OUTPUTS: * none * * RETURNS: * none * * EXTERNALLY READ: * none * * EXTERNALLY MODIFIED: * logstart - (static int) current starting index in display area * * DESCRIPTION: * Callback for general scrollbar motion in message area. */ static void logScrollCB(Widget w, char *client_data, XmScrollBarCallbackStruct *call_data) { #if defined(spr) && defined(DEBUG) (void)w; /* parameter not used */ (void)client_data; /* parameter not used */ #endif logstart = call_data->value; addMessageExpose(); } /*********************************************************************** * * FUNCTION: * resizeCB * * INPUTS: * w - (Widget) drawing area widget * client_data - (XtPointer) not used * call_data - (XmDrawingAreaCallbackStruct) not used * * OUTPUTS: * none * * RETURNS: * none * * EXTERNALLY READ: * data - (static SRData) subresource data for message area * cwidth - (static int) character width in pixels * * EXTERNALLY MODIFIED: * width - (static Dimension) current width of message area * columns - (static int) number of columns in message area * logScroll - (static Widget) scrollbar widget * logbuf - (static char *) internal buffer for log messages * logorg - (static int) current origin of log (circular queue) * logsize - (static int) current size of the log * logstart - (static int) current starting index in display area * sel_buf - (static char *) text buffer for selection * * DESCRIPTION: * Resize and reload the save and select buffers whenever the * widget is resized. */ static void resizeCB(Widget w, XtPointer client_data, XmDrawingAreaCallbackStruct *call_data) { Cardinal n; Arg args[1]; int old_columns; char *old_logbuf; int old_logorg; int old_logsize; int i; char *msg; char *tmp; int new_value; int new_slider; int value; int slider; int incr; int page; #if defined(spr) && defined(DEBUG) (void)client_data; /* parameter not used */ (void)call_data; /* parameter not used */ #endif if (logbuf == NULL) return; n = 0; XtSetArg(args[n], XmNwidth, &width); n++; XtGetValues(w, args, n); /* * save existing buffer */ old_columns = columns; old_logbuf = logbuf; old_logorg = logorg; old_logsize = logsize; /* * create a new empty buffer */ columns = ((int)width - 2 * HORZ_MARGIN) / cwidth; logbuf = (char *)XtMalloc((Cardinal)(data.save * (columns + 2))); if (logbuf == NULL) { fprintf(stderr, "Unable to create message buffer:\n"); fprintf(stderr, " lines = %d, columns = %d\n", data.save, columns); } logorg = 0; logsize = 0; logstart = 0; /* * reload the circular list (not very efficient, but rarely called) * solves the problem that some lines may have to be joined together */ tmp = (char *)XtMalloc((Cardinal)(old_columns + 2)); tmp = NULL; for (i = 0; i < old_logsize; ++i) { msg = old_logbuf + (((i + old_logorg) % old_logsize) * (old_columns + 2)); if (tmp != NULL) { tmp = (char *)XtRealloc(tmp, (Cardinal)(strlen(tmp) + strlen(msg) + 1)); (void)strcat(tmp, msg); if (strchr(tmp, '\n') != NULL) { addToLog(tmp); XtFree(tmp); tmp = NULL; } } else { if (strchr(msg, '\n') == NULL) { tmp = XtNewString(msg); } else { addToLog(msg); } } } XtFree(old_logbuf); /* * this code and logic follows from doMessage */ if (logsize > data.lines) { new_value = logsize - data.lines; new_slider = data.save - new_value; } else { new_value = 0; new_slider = data.save; } logstart = new_value; addMessageExpose(); if (logScroll != NULL) { /* * fix scroll bar */ XmScrollBarGetValues(logScroll, &value, &slider, &incr, &page); if ((value != new_value) || (slider != new_slider)) { XmScrollBarSetValues(logScroll, new_value, new_slider, incr, page, False); } } /* * reset the selection area - if the new selection area is smaller * than the old one, then we keep the end of the buffer */ tmp = sel_buf; i = strlen(tmp) + 1; if (i > data.lines * (columns + 2)) { msg = tmp + i - data.lines * (columns + 2); } else { msg = tmp; } sel_buf = (char *)XtMalloc((Cardinal)(data.lines * (columns + 2))); if (sel_buf == NULL) { fprintf(stderr, "Unable to create selection buffer:\n"); fprintf(stderr, " lines = %d, columns = %d\n", data.lines, columns); } else { strcpy(sel_buf, msg); } XtFree(tmp); } /*********************************************************************** * * FUNCTION: * inputCB * * INPUTS: * w - (Widget) message area * client_data - (XtPointer) not used * call_data - (XmDrawingAreaCallbackStruct) position * * OUTPUTS: * none * * RETURNS: * none * * EXTERNALLY READ: * display - (static Display *) pointer to display structure * win - (static Window) drawing window for X * * EXTERNALLY MODIFIED: * sel_active - (static int) True when select is being done (EH) * sel_start_row - (static int) row of first selected character * sel_start_col - (static int) column of first selected character * sel_cur_row - (static int) row currently under the cursor * sel_cur_col - (static int) column currently under the cursor * * DESCRIPTION: * Start end end selection of text from the message area. */ static void inputCB(Widget w, XtPointer client_data, XmDrawingAreaCallbackStruct *call_data) { int row, col; #if defined(spr) && defined(DEBUG) (void)client_data; /* parameter not used */ #endif if (!((call_data->event->type == ButtonPress) || (call_data->event->type == ButtonRelease))) return; if (call_data->event->xbutton.button != Button1) return; locate(&row, &col, call_data->event->xbutton.x, call_data->event->xbutton.y); if (call_data->event->type == ButtonPress) { sel_start_row = row; sel_start_col = col; sel_cur_row = row; sel_cur_col = col; setSelect(); XtAddEventHandler(w, Button1MotionMask, False, (XtEventHandler)selectEH, NULL); sel_active = True; } else { if ((sel_cur_row != row) || (sel_cur_col != col)) { sel_cur_row = row; sel_cur_col = col; setSelect(); } copySelect(); XtRemoveEventHandler(w, Button1MotionMask, False, (XtEventHandler)selectEH, NULL); sel_active = False; XtOwnSelection(w, XA_PRIMARY, CurrentTime, (XtConvertSelectionProc)convertCB, loseSelCB, NULL); } } /*********************************************************************** * * FUNCTION: * locate * * INPUTS: * x - (int) horizontal position in window * y - (int) vertical position in window * * OUTPUTS: * *row - (int) row from 0 to (data.lines - 1) * *col - (int) column from 0 to (columns - 1) * * RETURNS: * none * * EXTERNALLY READ: * data.lines - (static int) number of rows in message area * columns - (static int) number of columns in message area * cell_x - (static int) x origin of character cells * cell_y - (static int) y origin of character cells * cwidth - (static int) width of character cells * cheight - (static int) height of character cells * * EXTERNALLY MODIFIED: * none * * DESCRIPTION: * Translate (x, y) coordinates in message area drawing area * into row and column indices. */ static void locate(int *row, int *col, int x, int y) { *col = (x - cell_x) / cwidth; if (*col < 0) *col = 0; if (*col >= columns) *col = columns - 1; *row = (y - cell_y) / cheight; if (*row < 0) *row = 0; if (*row >= data.lines) *row = data.lines - 1; } /*********************************************************************** * * FUNCTION: * selectEH * * INPUTS: * w - (Widget) not used * client_data - (XtPointer) not used * event - (XEvent *) location * * OUTPUTS: * none * * RETURNS: * none * * EXTERNALLY READ: * * EXTERNALLY MODIFIED: * sel_cur_row - (static int) row currently under the cursor * sel_cur_col - (static int) column currently under the cursor * * DESCRIPTION: * Track mouse motion while capturing selected text. */ static void selectEH(Widget w, XtPointer client_data, XEvent *event) { int row, col; #if defined(spr) && defined(DEBUG) (void)w; /* parameter not used */ (void)client_data; /* parameter not used */ #endif locate(&row, &col, event->xmotion.x, event->xmotion.y); if ((sel_cur_row != row) || (sel_cur_col != col)) { sel_cur_row = row; sel_cur_col = col; setSelect(); } } /*********************************************************************** * * FUNCTION: * loseSelCB * * INPUTS: * w - (Widget) not used * atom - (Atom *) not used * * OUTPUTS: * none * * RETURNS: * none * * EXTERNALLY READ: * display - (static Display *) pointer to display structure * win - (static Window) drawing window for X * gc - (static GC) graphics context for window * normalFG - (static Pixel) normal foreground index * normalBG - (static Pixel) normal background index * height - (static Dimension) current height of message area * width - (static Dimension) current width of message area * * EXTERNALLY MODIFIED: * sel_rgn - (static Region) Region defining the select area * * DESCRIPTION: * This routine releases the selection when requested to do so. */ static void loseSelCB(Widget w, Atom *atom) { #if defined(spr) && defined(DEBUG) (void)w; /* parameter not used */ (void)atom; /* parameter not used */ #endif /* * release the selection */ if (sel_rgn != None) { XSetRegion(display, gc, sel_rgn); XSetForeground(display, gc, normalBG); XFillRectangle(display, win, gc, 0, 0, (unsigned int)width, (unsigned int)height); XSetForeground(display, gc, normalFG); drawText(); XDestroyRegion(sel_rgn); sel_rgn = None; } } /*********************************************************************** * * FUNCTION: * convertCB * * INPUTS: * w - (Widget) the drawing area * selection - (Atom *) selection requested * target - (Atom *) type of data requested * client_data - (XtPointer) not used * event - (XEvent *) not used * * OUTPUTS: * type - (Atom *) type of data returned * value - (char **) data returned * length - (unsigned long *) length of data returned * format - (int *) size of each data element (8) * * RETURNS: * True - (Boolean) on success * False - (Boolean) on failure * * EXTERNALLY READ: * sel_buf - (static char *) text buffer for selection * * EXTERNALLY MODIFIED: * none * * DESCRIPTION: * This routine transmits selected text when requested to do so. */ static Boolean convertCB(Widget w, Atom *selection, Atom *target, Atom *type, char **value, unsigned long *length, int *format) { #if defined(spr) && defined(DEBUG) (void)w; /* parameter not used */ #endif if ((*selection == XA_PRIMARY) && (*target == XA_STRING)) { *type = XA_STRING; *value = XtNewString(sel_buf); *length = strlen(sel_buf); *format = 8; return True; } else { return False; } } /*********************************************************************** * * FUNCTION: * setSelect * * INPUTS: * none * * OUTPUTS: * none * * RETURNS: * none * * EXTERNALLY READ: * display - (static Display *) pointer to display structure * win - (static Window) drawing window for X * gc - (static GC) graphics context for window * normalFG - (static Pixel) normal foreground index * normalBG - (static Pixel) normal background index * selectFG - (static Pixel) select foreground index * selectBG - (static Pixel) select background index * height - (static Dimension) current height of message area * width - (static Dimension) current width of message area * columns - (static int) number of columns in message area * sel_start_row - (static int) row of first selected character * sel_start_col - (static int) column of first selected character * sel_cur_row - (static int) row currently under the cursor * sel_cur_col - (static int) column currently under the cursor * * EXTERNALLY MODIFIED: * sel_rgn - (static Region) Region defining the select area * * DESCRIPTION: * Create a region on the message area corresponding to the * selected text, and refresh the necessary sections of the * message area. */ static void setSelect(void) { int r1, c1; /* first cell */ int r2, c2; /* last cell */ Region new_rgn; /* new select region */ Region chg_rgn; /* change region */ Region clp_rgn; /* clip region */ if ((sel_cur_row > sel_start_row) || ((sel_cur_row == sel_start_row) && (sel_cur_col >= sel_start_col))) { r1 = sel_start_row; c1 = sel_start_col; r2 = sel_cur_row; c2 = sel_cur_col; } else { r1 = sel_cur_row; c1 = sel_cur_col; r2 = sel_start_row; c2 = sel_start_col; } new_rgn = XCreateRegion(); if (r1 == r2) { appendToRegion(new_rgn, r1, c1, r2, c2); } else { appendToRegion(new_rgn, r1, c1, r1, columns - 1); appendToRegion(new_rgn, r1 + 1, 0, r2 - 1, columns - 1); appendToRegion(new_rgn, r2, 0, r2, c2); } if (sel_rgn == None) sel_rgn = XCreateRegion(); chg_rgn = XCreateRegion(); XXorRegion(sel_rgn, new_rgn, chg_rgn); XDestroyRegion(sel_rgn); sel_rgn = None; /* * redraw any selected text */ clp_rgn = XCreateRegion(); XIntersectRegion(new_rgn, chg_rgn, clp_rgn); XSetRegion(display, gc, clp_rgn); XSetForeground(display, gc, selectBG); XFillRectangle(display, win, gc, 0, 0, (unsigned int)width, (unsigned int)height); XSetForeground(display, gc, selectFG); drawText(); XDestroyRegion(clp_rgn); /* * redraw any normal text */ clp_rgn = XCreateRegion(); XSubtractRegion(chg_rgn, new_rgn, clp_rgn); XSetRegion(display, gc, clp_rgn); XSetForeground(display, gc, normalBG); XFillRectangle(display, win, gc, 0, 0, (unsigned int)width, (unsigned int)height); XSetForeground(display, gc, normalFG); drawText(); XDestroyRegion(clp_rgn); XDestroyRegion(chg_rgn); sel_rgn = new_rgn; } /*********************************************************************** * * FUNCTION: * copySelect * * INPUTS: * none * * OUTPUTS: * none * * RETURNS: * none * * EXTERNALLY READ: * logstart - (static int) current starting index in display area * sel_start_row - (static int) row of first selected character * sel_start_col - (static int) column of first selected character * sel_cur_row - (static int) row currently under the cursor * sel_cur_col - (static int) column currently under the cursor * * EXTERNALLY MODIFIED: * sel_buf - (static char *) text buffer for selection * * DESCRIPTION: * Copy the text indicated by the selected area of the screen * to the select text buffer. */ static void copySelect(void) { int r1, c1; /* first cell */ int r2, c2; /* last cell */ char *msg; /* text pointer */ unsigned int len; /* length of text string */ char *ptr; /* pointer into selection buffer */ if ((sel_cur_row > sel_start_row) || ((sel_cur_row == sel_start_row) && (sel_cur_col >= sel_start_col))) { r1 = sel_start_row; c1 = sel_start_col; r2 = sel_cur_row; c2 = sel_cur_col; } else { r1 = sel_cur_row; c1 = sel_cur_col; r2 = sel_start_row; c2 = sel_start_col; } ptr = sel_buf; /* * get the first line */ msg = getLogMessage(logstart + r1); len = strlen(msg); if (c1 > (int)len) { msg = ""; len = 0; } else { msg += c1; len -= c1; if (r1 == r2) c2 -= c1; } /* * copy each line in turn and get the last line */ while (++r1 <= r2) { strncpy(ptr, msg, len); ptr += len; msg = getLogMessage(logstart + r1); len = strlen(msg); } /* * copy out the last line */ if (c2 + 1 < (int)len) { len = c2 + 1; } strncpy(ptr, msg, len); ptr += len; /* * terminate the buffer */ *ptr = '\0'; /* * cannot set a property to an empty string */ if (*sel_buf == '\0') { strcpy(sel_buf, "\n"); } } /*********************************************************************** * * FUNCTION: * appendToRegion * * INPUTS: * rgn - (Region) existing and modified region * r1 - (int) row number of upper left corner * c1 - (int) column number of upper left corner * r2 - (int) row number of lower right corner * c2 - (int) column number of lower right corner * * OUTPUTS: * none * * RETURNS: * none * * EXTERNALLY READ: * height - (static Dimension) current height of message area * width - (static Dimension) current width of message area * cheight - (static int) character height in pixels * cwidth - (static int) character width in pixels * cell_x - (static int) x offset of first cell in pixels * cell_y - (static int) y offset of first cell in pixels * * EXTERNALLY MODIFIED: * none * * DESCRIPTION: * Append a rectangle defined by row and column values to the * select region. */ static void appendToRegion(Region rgn, int r1, int c1, int r2, int c2) { XRectangle r; if ((c2 >= c1) && (r2 >= r1)) { r.x = cwidth * c1 + cell_x; r.y = cheight * r1 + cell_y; r.width = cwidth * (1 + c2 - c1); r.height = cheight * (1 + r2 - r1); XUnionRectWithRegion(&r, rgn, rgn); } }