#if !defined(lint) && !defined(SABER) && 0
static char rcs_version[] = "$Athena: ScrollByL.c,v 4.5 88/12/19 13:46:04 kit Exp $";
#endif
#include <stdio.h>
#include <ctype.h>
#include <X11/Xos.h>
#include <stdlib.h>
#include <X11/IntrinsicP.h>
#include <sys/stat.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/Scrollbar.h>
#include <X11/Xmu/Misc.h>
#include "ScrollByLP.h"
static char defaultTranslations[] =
"<Key>f: Page(Forward) \n\
<Key>b: Page(Back) \n\
<Key>1: Page(Line, 1) \n\
<Key>2: Page(Line, 2) \n\
<Key>3: Page(Line, 3) \n\
<Key>4: Page(Line, 4) \n\
<Key>\\ : Page(Forward)";
#define Offset(field) XtOffset(ScrollByLineWidget, scroll.field)
#define CoreOffset(field) XtOffset(ScrollByLineWidget, core.field)
static XtResource resources[] = {
{XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
CoreOffset(width), XtRImmediate, (caddr_t) 500},
{XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
CoreOffset(height), XtRImmediate, (caddr_t) 700},
{XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
Offset(foreground), XtRString, "XtDefaultForeground"},
{XtNforceVert, XtCBoolean, XtRBoolean, sizeof(Boolean),
Offset(force_vert), XtRImmediate, (caddr_t) FALSE},
{XtNindent, XtCIndent, XtRDimension, sizeof(Dimension),
Offset(indent), XtRImmediate, (caddr_t) 15},
{XtNuseRight, XtCBoolean, XtRBoolean, sizeof(Boolean),
Offset(use_right), XtRImmediate, (caddr_t) FALSE},
{XtNmanualFontNormal, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
Offset(normal_font), XtRString, MANPAGE_NORMAL},
{XtNmanualFontBold, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
Offset(bold_font), XtRString, MANPAGE_BOLD},
{XtNmanualFontItalic, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
Offset(italic_font), XtRString, MANPAGE_ITALIC},
{XtNmanualFontSymbol, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
Offset(symbol_font), XtRString, MANPAGE_SYMBOL},
{XtNfile, XtCFile, XtRFile, sizeof(FILE *),
Offset(file), XtRImmediate, (caddr_t) NULL},
{XtNNumTotalLines, XtCNumTotalLines, XtRInt, sizeof(int),
Offset(lines), XtRImmediate, (caddr_t) 0},
{XtNNumVisibleLines, XtCNumVisibleLines, XtRInt, sizeof(int),
Offset(num_visible_lines), XtRImmediate, (caddr_t) 0},
};
#undef Offset
#undef CoreOffset
static void CreateScrollbar(Widget w);
static Boolean ScrollVerticalText(Widget w, int new_line, Boolean force_redisp);
static void Layout(Widget w);
static void LoadFile(Widget w);
static void MoveAndClearText(Widget w, int old_y, int height, int new_y);
static void PaintText(Widget w, int y_loc, int height);
static void PrintText(Widget w, int start_line, int num_lines, int location);
static void SetThumbHeight(Widget w);
static void VerticalJump(Widget w, XtPointer junk, XtPointer percent_ptr);
static void VerticalScroll(Widget w, XtPointer client_data, XtPointer call_data);
static void Realize(Widget w, Mask *valueMask, XSetWindowAttributes *attributes);
static void Initialize(Widget req, Widget new, ArgList args, Cardinal *num_args);
static void Destroy(Widget w);
static void Redisplay(Widget w, XEvent *event, Region region);
static void Page(Widget w, XEvent * event, String * params, Cardinal *num_params);
static Boolean SetValuesHook(Widget w, ArgList args, Cardinal *num_args);
static XtActionsRec actions[] = {
{ "Page", Page},
};
#define superclass (&simpleClassRec)
#define SuperClass simpleWidgetClass
ScrollByLineClassRec scrollByLineClassRec = {
{
(WidgetClass) superclass,
"ScrollByLine",
sizeof(ScrollByLineRec),
NULL,
NULL,
FALSE,
Initialize,
NULL,
Realize,
actions,
XtNumber(actions),
resources,
XtNumber(resources),
NULLQUARK,
TRUE,
FALSE,
TRUE,
FALSE,
Destroy,
Layout,
Redisplay,
NULL,
SetValuesHook,
XtInheritSetValuesAlmost,
NULL,
NULL,
XtVersion,
NULL,
defaultTranslations,
XtInheritQueryGeometry,
XtInheritDisplayAccelerator,
NULL,
},
{
XtInheritChangeSensitive
}
};
WidgetClass scrollByLineWidgetClass =
(WidgetClass) &scrollByLineClassRec;
static void
Layout(Widget w)
{
ScrollByLineWidget sblw = (ScrollByLineWidget) w;
Dimension width, height;
Widget bar;
Position bar_bw;
CreateScrollbar(w);
bar = sblw->scroll.bar;
height = sblw->core.height;
width = sblw->core.width;
bar_bw = bar->core.border_width;
if (sblw->scroll.use_right) {
XtMoveWidget(bar, width - (bar->core.width + bar_bw), - bar_bw);
sblw->scroll.offset = 0;
}
else {
XtMoveWidget(bar, - bar_bw, - bar_bw);
sblw->scroll.offset = bar->core.width + bar_bw;
}
XtResizeWidget(bar, bar->core.width, height, bar->core.border_width);
SetThumbHeight(w);
sblw->scroll.num_visible_lines = height / sblw->scroll.font_height + 1;
}
static void
GExpose(Widget w, XtPointer junk, XEvent *event, Boolean *cont)
{
if (event->type == GraphicsExpose)
Redisplay(w, event, NULL);
}
static void
Redisplay(Widget w, XEvent *event, Region region)
{
int top, height;
if (event->type == Expose) {
top = event->xexpose.y;
height = event->xexpose.height;
}
else {
top = event->xgraphicsexpose.y;
height = event->xgraphicsexpose.height;
}
PaintText(w, top, height);
}
static void
PaintText(Widget w, int y_loc, int height)
{
ScrollByLineWidget sblw = (ScrollByLineWidget) w;
int start_line, location;
start_line = y_loc / sblw->scroll.font_height + sblw->scroll.line_pointer;
if (start_line >= sblw->scroll.lines)
return;
location = y_loc / sblw->scroll.font_height * sblw->scroll.font_height;
PrintText(w, start_line, sblw->scroll.num_visible_lines, location);
}
static void
Page(Widget w, XEvent * event, String * params, Cardinal *num_params)
{
ScrollByLineWidget sblw = (ScrollByLineWidget) w;
Widget bar = sblw->scroll.bar;
if (*num_params < 1)
return;
if (bar == (Widget) NULL)
return;
switch ( params[0][0] ) {
case 'f':
case 'F':
VerticalScroll(bar, NULL, (XtPointer)((long) bar->core.height));
break;
case 'b':
case 'B':
VerticalScroll(bar, NULL, (XtPointer)(- (long) bar->core.height));
break;
case 'L':
case 'l':
VerticalScroll(bar, NULL,
(XtPointer)((long) atoi(params[1]) * sblw->scroll.font_height));
break;
default:
return;
}
}
static void
CreateScrollbar(Widget w)
{
ScrollByLineWidget sblw = (ScrollByLineWidget) w;
Arg args[5];
Cardinal num_args = 0;
if (sblw->scroll.bar != NULL)
return;
XtSetArg(args[num_args], XtNorientation, XtorientVertical); num_args++;
sblw->scroll.bar = XtCreateWidget("scrollbar", scrollbarWidgetClass, w,
args, num_args);
XtAddCallback(sblw->scroll.bar, XtNscrollProc, VerticalScroll, NULL);
XtAddCallback(sblw->scroll.bar, XtNjumpProc, VerticalJump, NULL);
}
static Boolean
ScrollVerticalText(
Widget w,
int new_line,
Boolean force_redisp)
{
ScrollByLineWidget sblw = (ScrollByLineWidget) w;
int num_lines = sblw->scroll.num_visible_lines;
int max_lines, old_line;
Boolean move_thumb = FALSE;
if ( new_line < 0) {
new_line = 0;
move_thumb = TRUE;
}
else {
max_lines = sblw->scroll.lines - (int)w->core.height / sblw->scroll.font_height;
AssignMax(max_lines, 0);
if ( new_line > max_lines ) {
new_line = max_lines;
move_thumb = TRUE;
}
}
old_line = sblw->scroll.line_pointer;
sblw->scroll.line_pointer = new_line;
if (force_redisp)
MoveAndClearText(w, 0, 0, 0);
if (new_line == old_line)
return(move_thumb);
else if (new_line < old_line) {
int lines_to_scroll = old_line - new_line;
MoveAndClearText(w, 0, num_lines - lines_to_scroll, lines_to_scroll);
}
else {
int lines_to_scroll = new_line - old_line;
MoveAndClearText(w, lines_to_scroll, num_lines - lines_to_scroll, 0);
}
return(move_thumb);
}
static void
MoveAndClearText(Widget w, int old_y, int height, int new_y)
{
ScrollByLineWidget sblw = (ScrollByLineWidget) w;
int from_left = sblw->scroll.indent + sblw->scroll.offset - 1;
int y_clear;
old_y *= sblw->scroll.font_height;
new_y *= sblw->scroll.font_height;
height *= sblw->scroll.font_height;
if (height <= sblw->scroll.font_height) {
XClearArea( XtDisplay(w), XtWindow(w), from_left, 0,
(unsigned int) 0, (unsigned int) 0, FALSE);
PaintText(w, 0, (int) sblw->core.height);
return;
}
if ((int)height + (int)old_y > (int)w->core.height)
height = w->core.height - old_y;
XCopyArea(XtDisplay(w), XtWindow(w), XtWindow(w), sblw->scroll.move_gc,
from_left, old_y,
(unsigned int) w->core.width - from_left, (unsigned int) height,
from_left, new_y);
if (old_y > new_y)
height -= sblw->scroll.font_height/2;
else
height -= sblw->scroll.font_height;
if (old_y > new_y)
y_clear = height;
else
y_clear = 0;
XClearArea( XtDisplay(w), XtWindow(w), from_left, y_clear,
(unsigned int) 0, (unsigned int) (w->core.height - height),
FALSE);
PaintText(w, (int) y_clear, (int) (w->core.height - height));
}
static void
SetThumbHeight(Widget w)
{
ScrollByLineWidget sblw = (ScrollByLineWidget) w;
float shown;
if (sblw->scroll.bar == NULL)
return;
if (sblw->scroll.lines == 0)
shown = 1.0;
else
shown = (float) w->core.height / (float) (sblw->scroll.lines *
sblw->scroll.font_height);
if (shown > 1.0)
shown = 1.0;
XawScrollbarSetThumb( sblw->scroll.bar, (float) -1, shown );
}
static void
SetThumb(Widget w)
{
float location;
ScrollByLineWidget sblw = (ScrollByLineWidget) w;
if ( (sblw->scroll.lines == 0) || (sblw->scroll.bar == NULL) )
return;
location = (float) sblw->scroll.line_pointer / (float) sblw->scroll.lines;
XawScrollbarSetThumb( sblw->scroll.bar, location , (float) -1 );
}
static void
VerticalJump(Widget w, XtPointer junk, XtPointer percent_ptr)
{
float percent = *((float *) percent_ptr);
int new_line;
ScrollByLineWidget sblw = (ScrollByLineWidget) XtParent(w);
new_line = (int) ((float) sblw->scroll.lines * percent);
if (ScrollVerticalText( (Widget) sblw, new_line, FALSE))
SetThumb((Widget) sblw);
}
static void
VerticalScroll(Widget w, XtPointer client_data, XtPointer call_data)
{
int pos = (int)(long) call_data;
int new_line;
ScrollByLineWidget sblw = (ScrollByLineWidget) XtParent(w);
new_line = sblw->scroll.line_pointer + (pos / sblw->scroll.font_height);
(void) ScrollVerticalText( (Widget) sblw, new_line, FALSE);
SetThumb( (Widget) sblw);
}
static void
Initialize(Widget req, Widget new, ArgList args, Cardinal *num_args)
{
ScrollByLineWidget sblw = (ScrollByLineWidget) new;
unsigned long figWidth;
Atom atomNum;
sblw->scroll.top_line = NULL;
sblw->scroll.line_pointer = 0;
LoadFile(new);
sblw->scroll.bar = (Widget) NULL;
sblw->scroll.font_height = (sblw->scroll.normal_font->max_bounds.ascent +
sblw->scroll.normal_font->max_bounds.descent);
atomNum = XInternAtom(XtDisplay(req), "FIGURE_WIDTH", False);
if (XGetFontProperty(sblw->scroll.normal_font, atomNum, &figWidth))
sblw->scroll.h_width = figWidth;
else
sblw->scroll.h_width = XTextWidth(sblw->scroll.normal_font, "$", 1);
}
static void
CreateGCs(Widget w)
{
ScrollByLineWidget sblw = (ScrollByLineWidget) w;
XtGCMask mask;
XGCValues values;
values.graphics_exposures = TRUE;
sblw->scroll.move_gc = XtGetGC(w, GCGraphicsExposures, &values);
mask = GCForeground | GCFont;
values.foreground = sblw->scroll.foreground;
values.font = sblw->scroll.normal_font->fid;
sblw->scroll.normal_gc = XtGetGC(w, mask, &values);
values.font = sblw->scroll.italic_font->fid;
sblw->scroll.italic_gc = XtGetGC(w, mask, &values);
values.font = sblw->scroll.bold_font->fid;
sblw->scroll.bold_gc = XtGetGC(w, mask, &values);
values.font = sblw->scroll.symbol_font->fid;
sblw->scroll.symbol_gc = XtGetGC(w, mask, &values);
}
static void
DestroyGCs(Widget w)
{
ScrollByLineWidget sblw = (ScrollByLineWidget) w;
XtReleaseGC(w, sblw->scroll.normal_gc);
XtReleaseGC(w, sblw->scroll.bold_gc);
XtReleaseGC(w, sblw->scroll.italic_gc);
XtReleaseGC(w, sblw->scroll.move_gc);
}
static void
Realize(Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
{
ScrollByLineWidget sblw = (ScrollByLineWidget) w;
CreateScrollbar(w);
CreateGCs(w);
Layout(w);
(*SuperClass->core_class.realize) (w, valueMask, attributes);
XtRealizeWidget(sblw->scroll.bar);
XtMapWidget(sblw->scroll.bar);
XtAddEventHandler(w, 0, TRUE, GExpose, NULL);
}
static void
Destroy(Widget w)
{
ScrollByLineWidget sblw = (ScrollByLineWidget) w;
if (sblw->scroll.bar != NULL)
XtDestroyWidget(sblw->scroll.bar);
if (sblw->scroll.file != NULL)
fclose(sblw->scroll.file);
DestroyGCs(w);
}
static Boolean
SetValuesHook(Widget w, ArgList args, Cardinal *num_args)
{
Boolean ret = TRUE;
Cardinal i;
for (i = 0; i < *num_args; i++) {
if (strcmp(XtNfile, args[i].name) == 0) {
LoadFile(w);
ret = TRUE;
}
}
return(ret);
}
#define ADD_MORE_MEM 100
#define CHAR_PER_LINE 40
static void
LoadFile(Widget w)
{
ScrollByLineWidget sblw = (ScrollByLineWidget) w;
FILE * file = sblw->scroll.file;
char *page;
char **line_pointer,**top_line;
int nlines;
struct stat fileinfo;
if ( sblw->scroll.top_line != NULL) {
XtFree(*(sblw->scroll.top_line));
XtFree((char *)(sblw->scroll.top_line));
}
sblw->scroll.top_line = NULL;
if (file == NULL)
return;
if (fstat(fileno(file), &fileinfo))
XtAppError(XtWidgetToApplicationContext(w),
"SBLW LoadFile: Failure in fstat.");
if ( (nlines = fileinfo.st_size/CHAR_PER_LINE) == 0)
return;
page = XtMalloc(fileinfo.st_size + 1);
top_line = line_pointer = (char**) XtMalloc( nlines * sizeof(char *) );
fseek(file, 0L, SEEK_SET);
if (fread(page, sizeof(char), fileinfo.st_size, file) == 0)
XtAppError(XtWidgetToApplicationContext(w),
"SBLW LoadFile: Failure in fread.");
*(page + fileinfo.st_size) = '\0';
*line_pointer++ = page;
while (*page != '\0') {
if ( *page == '\n' ) {
*line_pointer++ = page + 1;
if (line_pointer >= top_line + nlines) {
top_line = (char **) XtRealloc( (char *)top_line, (nlines +
ADD_MORE_MEM) * sizeof(char *) );
line_pointer = top_line + nlines;
nlines += ADD_MORE_MEM;
}
}
page++;
}
sblw->scroll.lines = nlines = line_pointer - top_line - 1;
top_line = (char **) XtRealloc((char *)top_line, nlines * sizeof(char *));
sblw->scroll.top_line = top_line;
sblw->scroll.line_pointer = 0;
SetThumbHeight(w);
SetThumb(w);
}
#define NLINES 66
#define BACKSPACE 010
#define NORMAL 0
#define BOLD 1
#define ITALIC 2
#define SYMBOL 3
#define WHICH(italic, bold) ((bold) ? BOLD : ((italic) ? ITALIC : NORMAL))
static int DumpText(Widget w, int x_loc, int y_loc, char * buf, int len, int format);
static Boolean Boldify(char *);
static void
PrintText(Widget w, int start_line, int num_lines, int location)
{
ScrollByLineWidget sblw = (ScrollByLineWidget) w;
register char *bufp, *c;
int current_line;
char buf[BUFSIZ];
Boolean italicflag = FALSE;
Boolean first = TRUE;
int x_loc, y_loc;
int h_col, h_fix;
char * h_c;
if (sblw->scroll.top_line == NULL || num_lines == 0)
return;
current_line = start_line;
c = *(sblw->scroll.top_line + start_line);
y_loc = location + sblw->scroll.normal_font->max_bounds.ascent;
bufp = buf;
x_loc = sblw->scroll.offset + sblw->scroll.indent;
h_col = 0;
for (h_fix = 1; h_fix <= (start_line % NLINES); h_fix++)
if (**(sblw->scroll.top_line + start_line - h_fix) != '\n')
{
first = FALSE;
break;
}
while(TRUE) {
if (current_line % NLINES == 0)
first = TRUE;
if ( (bufp - buf) > (BUFSIZ - 10) )
while ( (*c != '\n') && (*c != '\0') ) c++;
switch(*c) {
case '\0':
DumpText(w, x_loc, y_loc, buf, bufp - buf, WHICH(italicflag, first));
return;
case '\n':
if (bufp != buf) {
Boolean bold;
*bufp = '\0';
bold = ( (first) || ((x_loc == (sblw->scroll.offset +
sblw->scroll.indent)) && Boldify(buf)) );
(void) DumpText(w, x_loc, y_loc, buf, bufp - buf,
WHICH(italicflag, bold));
if (bold)
first = FALSE;
}
if (++current_line == start_line + num_lines )
return;
bufp = buf;
italicflag = FALSE;
x_loc = sblw->scroll.offset + sblw->scroll.indent;
h_col = 0;
y_loc += sblw->scroll.font_height;
break;
case '\t':
x_loc = DumpText(w, x_loc, y_loc, buf, bufp - buf,
WHICH(italicflag, first));
h_col += bufp - buf;
bufp = buf;
italicflag = FALSE;
x_loc = sblw->scroll.offset + sblw->scroll.indent;
h_col = h_col + 8 - (h_col%8);
x_loc += sblw->scroll.h_width * h_col;
break;
case ' ':
h_c = c + 1;
while (*h_c == ' ') h_c++;
if (h_c - c < 4)
{
*bufp++ = *c;
break;
}
x_loc = DumpText(w, x_loc, y_loc, buf, bufp - buf,
WHICH(italicflag, first));
h_col += bufp - buf;
bufp = buf;
italicflag = FALSE;
x_loc = sblw->scroll.offset + sblw->scroll.indent;
h_col += (h_c - c);
x_loc += sblw->scroll.h_width * h_col;
c = h_c - 1;
break;
case '\033':
c++;
break;
case BACKSPACE:
if (c[-1] == c[1] && c[1] != BACKSPACE) {
if (bufp > buf) {
bufp--;
x_loc = DumpText(w, x_loc, y_loc, buf, bufp - buf,
WHICH(italicflag, FALSE));
h_col += bufp - buf;
}
bufp = buf;
*bufp++ = c[1];
x_loc = DumpText(w, x_loc, y_loc, buf, bufp - buf, BOLD);
h_col += bufp - buf;
bufp = buf;
first = FALSE;
while (*c == BACKSPACE && c[-1] == c[1])
c += 2;
c--;
}
else {
if ((c[-1] == 'o' && c[1] == '+')
|| (c[-1] == '+' && c[1] == 'o')) {
if (bufp>buf) {
bufp--;
x_loc = DumpText(w, x_loc, y_loc, buf, bufp - buf,
WHICH(italicflag, FALSE));
h_col += bufp - buf;
}
bufp = buf;
*bufp = (char)183;
x_loc = DumpText(w, x_loc, y_loc, buf, 1, SYMBOL);
h_col++;
c++;
}
else {
if (bufp>buf)
bufp--;
}
}
break;
case '_':
if(*(c + 1) == BACKSPACE) {
if(!italicflag) {
if (bufp != buf) {
x_loc = DumpText(w, x_loc, y_loc, buf, bufp - buf, NORMAL);
h_col += bufp - buf;
bufp = buf;
}
italicflag = TRUE;
}
c += 2;
*bufp++ = *c;
break;
}
default:
if(italicflag) {
if (bufp != buf) {
x_loc = DumpText(w, x_loc, y_loc, buf, bufp - buf,
WHICH(italicflag, FALSE));
h_col += bufp - buf;
bufp = buf;
}
italicflag = FALSE;
}
*bufp++ = *c;
break;
}
c++;
}
}
static int
DumpText(Widget w, int x_loc, int y_loc, char * buf, int len, int format)
{
ScrollByLineWidget sblw = (ScrollByLineWidget) w;
GC gc;
XFontStruct * font;
switch(format) {
case ITALIC:
gc = sblw->scroll.italic_gc;
font = sblw->scroll.italic_font;
break;
case BOLD:
gc = sblw->scroll.bold_gc;
font = sblw->scroll.bold_font;
break;
case SYMBOL:
gc = sblw->scroll.symbol_gc;
font = sblw->scroll.symbol_font;
break;
default:
gc = sblw->scroll.normal_gc;
font = sblw->scroll.normal_font;
break;
}
XDrawString(XtDisplay(w), XtWindow(w), gc, x_loc, y_loc, buf, len);
return(x_loc + XTextWidth(font, buf, len));
}
static Boolean
Boldify(register char *sp)
{
register char *sp_pointer;
int length,count;
length = strlen(sp);
for (sp_pointer = sp, count = 0; count < length; sp_pointer++,count++)
if ( !isupper(*sp_pointer) && !isspace(*sp_pointer) )
return(0);
return(1);
}
#undef superclass
#undef SuperClass