1/*
2
3Copyright (c) 1987, 1988  X Consortium
4
5Permission is hereby granted, free of charge, to any person obtaining
6a copy of this software and associated documentation files (the
7"Software"), to deal in the Software without restriction, including
8without limitation the rights to use, copy, modify, merge, publish,
9distribute, sublicense, and/or sell copies of the Software, and to
10permit persons to whom the Software is furnished to do so, subject to
11the following conditions:
12
13The above copyright notice and this permission notice shall be included
14in all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
20OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22OTHER DEALINGS IN THE SOFTWARE.
23
24Except as contained in this notice, the name of the X Consortium shall
25not be used in advertising or otherwise to promote the sale, use or
26other dealings in this Software without prior written authorization
27from the X Consortium.
28
29*/
30
31#include <stdio.h>
32#include <ctype.h>
33#include <X11/Xos.h>
34#include <stdlib.h>
35#include <limits.h>
36
37#include <X11/IntrinsicP.h>
38#include <sys/stat.h>           /* depends on IntrinsicP.h */
39#include <X11/StringDefs.h>
40
41#include <X11/Xaw/Scrollbar.h>
42
43#include "ScrollByLP.h"
44
45/* Default Translation Table */
46
47static char defaultTranslations[] =
48  "<Key>f:      Page(Forward) \n\
49   <Key>b:      Page(Back) \n\
50   <Key>1:      Page(Line, 1) \n\
51   <Key>2:      Page(Line, 2) \n\
52   <Key>3:      Page(Line, 3) \n\
53   <Key>4:      Page(Line, 4) \n\
54   <Key>\\ :    Page(Forward)";
55
56/****************************************************************
57 *
58 * ScrollByLine Resources
59 *
60 ****************************************************************/
61
62#define Offset(field) XtOffset(ScrollByLineWidget, scroll.field)
63#define CoreOffset(field) XtOffset(ScrollByLineWidget, core.field)
64
65static XtResource resources[] = {
66    {XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
67     CoreOffset(width), XtRImmediate, (caddr_t) 500},
68    {XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
69     CoreOffset(height), XtRImmediate, (caddr_t) 700},
70
71    {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
72     Offset(foreground), XtRString, "XtDefaultForeground"},
73    {XtNforceVert, XtCBoolean, XtRBoolean, sizeof(Boolean),
74     Offset(force_vert), XtRImmediate, (caddr_t) FALSE},
75    {XtNindent, XtCIndent, XtRDimension, sizeof(Dimension),
76     Offset(indent), XtRImmediate, (caddr_t) 15},
77    {XtNuseRight, XtCBoolean, XtRBoolean, sizeof(Boolean),
78     Offset(use_right), XtRImmediate, (caddr_t) FALSE},
79    {XtNmanualFontNormal, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
80     Offset(normal_font), XtRString, MANPAGE_NORMAL},
81    {XtNmanualFontBold, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
82     Offset(bold_font), XtRString, MANPAGE_BOLD},
83    {XtNmanualFontItalic, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
84     Offset(italic_font), XtRString, MANPAGE_ITALIC},
85    {XtNmanualFontSymbol, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
86     Offset(symbol_font), XtRString, MANPAGE_SYMBOL},
87    {XtNfile, XtCFile, XtRFile, sizeof(FILE *),
88     Offset(file), XtRImmediate, (caddr_t) NULL},
89    {XtNNumTotalLines, XtCNumTotalLines, XtRInt, sizeof(int),
90     Offset(lines), XtRImmediate, (caddr_t) 0},
91    {XtNNumVisibleLines, XtCNumVisibleLines, XtRInt, sizeof(int),
92     Offset(num_visible_lines), XtRImmediate, (caddr_t) 0},
93};
94
95#undef Offset
96#undef CoreOffset
97
98/****************************************************************
99 *
100 * Full class record constant
101 *
102 ****************************************************************/
103
104static void CreateScrollbar(Widget w);
105static Boolean ScrollVerticalText(Widget w, int new_line, Boolean force_redisp);
106static void Layout(Widget w);
107static void LoadFile(Widget w);
108static void MoveAndClearText(Widget w, int old_y, int height, int new_y);
109static void PaintText(Widget w, int y_loc, int height);
110static void PrintText(Widget w, int start_line, int num_lines, int location);
111static void SetThumbHeight(Widget w);
112static void VerticalJump(Widget w, XtPointer junk, XtPointer percent_ptr);
113static void VerticalScroll(Widget w, XtPointer client_data,
114                           XtPointer call_data);
115
116/* semi - public functions. */
117
118static void Realize(Widget w, Mask *valueMask,
119                    XSetWindowAttributes *attributes);
120static void Initialize(Widget req, Widget new, ArgList args,
121                       Cardinal *num_args);
122static void Destroy(Widget w);
123static void Redisplay(Widget w, XEvent *event, Region region);
124static void Page(Widget w, XEvent *event, String *params, Cardinal *num_params);
125static Boolean SetValuesHook(Widget w, ArgList args, Cardinal *num_args);
126
127static XtActionsRec actions[] = {
128    {"Page", Page},
129};
130
131#define superclass		(&simpleClassRec)
132#define SuperClass		simpleWidgetClass
133
134ScrollByLineClassRec scrollByLineClassRec = {
135    {
136/* core_class fields      */
137     /* superclass         */ (WidgetClass) superclass,
138     /* class_name         */ "ScrollByLine",
139     /* widget_size        */ sizeof(ScrollByLineRec),
140     /* class_initialize   */ NULL,
141     /* class_part_init    */ NULL,
142     /* class_inited       */ FALSE,
143     /* initialize         */ Initialize,
144     /* initialize_hook    */ NULL,
145     /* realize            */ Realize,
146     /* actions            */ actions,
147     /* num_actions        */ XtNumber(actions),
148     /* resources          */ resources,
149     /* num_resources      */ XtNumber(resources),
150     /* xrm_class          */ NULLQUARK,
151     /* compress_motion    */ TRUE,
152     /* compress_exposure  */ FALSE,
153     /* compress_enterleave */ TRUE,
154     /* visible_interest   */ FALSE,
155     /* destroy            */ Destroy,
156     /* resize             */ Layout,
157     /* expose             */ Redisplay,
158     /* set_values         */ NULL,
159     /* set_values_hook    */ SetValuesHook,
160     /* set_values_almost  */ XtInheritSetValuesAlmost,
161     /* get_values_hook    */ NULL,
162     /* accept_focus       */ NULL,
163     /* version            */ XtVersion,
164     /* callback_private   */ NULL,
165     /* tm_table           */ defaultTranslations,
166     /* query_geometry     */ XtInheritQueryGeometry,
167     /* display_accelerator */ XtInheritDisplayAccelerator,
168     /* extension          */ NULL,
169     },
170    {
171/* simple fields */
172     /* change_sensitive         */ XtInheritChangeSensitive
173     }
174};
175
176WidgetClass scrollByLineWidgetClass = (WidgetClass) &scrollByLineClassRec;
177
178/****************************************************************
179 *
180 * Private Routines
181 *
182 ****************************************************************/
183
184/*	Function Name: Layout
185 *	Description: This function lays out the scroll widget.
186 *	Arguments: w - the scroll widget.
187 *                 key - a boolean: if true then resize the widget to the child
188 *                                  if false the resize children to fit widget.
189 *	Returns: TRUE if successful.
190 */
191
192static void
193Layout(Widget w)
194{
195    ScrollByLineWidget sblw = (ScrollByLineWidget) w;
196    Dimension width, height;
197    Widget bar;
198    Position bar_bw;
199
200    CreateScrollbar(w);
201
202/*
203 * For now always show the bar.
204 */
205
206    bar = sblw->scroll.bar;
207    height = sblw->core.height;
208    width = sblw->core.width;
209    bar_bw = bar->core.border_width;
210
211    /* Move child and v_bar to correct location. */
212    if (sblw->scroll.use_right) {
213        XtMoveWidget(bar, width - (bar->core.width + bar_bw), -bar_bw);
214        sblw->scroll.offset = 0;
215    }
216    else {
217        XtMoveWidget(bar, -bar_bw, -bar_bw);
218        sblw->scroll.offset = bar->core.width + bar_bw;
219    }
220
221    /* resize the scrollbar to be the correct height or width. */
222    XtResizeWidget(bar, bar->core.width, height, bar->core.border_width);
223
224    SetThumbHeight(w);
225
226    sblw->scroll.num_visible_lines = height / sblw->scroll.font_height + 1;
227}
228
229/* ARGSUSED */
230static void
231GExpose(Widget w, XtPointer junk, XEvent * event, Boolean * cont)
232{
233
234/*
235 * Graphics exposure events are not currently sent to exposure proc.
236 */
237
238    if (event->type == GraphicsExpose)
239        Redisplay(w, event, NULL);
240
241}                               /* ChildExpose */
242
243/*
244 * Repaint the widget's child Window Widget.
245 */
246
247/* ARGSUSED */
248static void
249Redisplay(Widget w, XEvent * event, Region region)
250{
251    int top, height;            /* the locations of the top and height
252                                   of the region that needs to be repainted. */
253
254/*
255 * This routine tells the client which sections of the window to
256 * repaint in his callback function which does the actual repainting.
257 */
258
259    if (event->type == Expose) {
260        top = event->xexpose.y;
261        height = event->xexpose.height;
262    }
263    else {
264        top = event->xgraphicsexpose.y;
265        height = event->xgraphicsexpose.height;
266    }
267
268    PaintText(w, top, height);
269}                               /* redisplay (expose) */
270
271/*	Function Name: PaintText
272 *	Description: paints the text at the give location for a given height.
273 *	Arguments: w - the sbl widget.
274 *                 y_loc, height - location and size of area to paint.
275 *	Returns: none
276 */
277
278static void
279PaintText(Widget w, int y_loc, int height)
280{
281    ScrollByLineWidget sblw = (ScrollByLineWidget) w;
282    int start_line, location;
283
284    start_line = y_loc / sblw->scroll.font_height + sblw->scroll.line_pointer;
285
286    if (start_line >= sblw->scroll.lines)
287        return;
288
289/*
290 * Only integer arithmetic makes this possible.
291 */
292
293    location = y_loc / sblw->scroll.font_height * sblw->scroll.font_height;
294
295    PrintText(w, start_line, sblw->scroll.num_visible_lines, location);
296}
297
298/*	Function Name: Page
299 *	Description: This function pages the widget, by the amount it receives
300 *                   from the translation Manager.
301 *	Arguments: w - the ScrollByLineWidget.
302 *                 event - the event that caused this return.
303 *                 params - the parameters passed to it.
304 *                 num_params - the number of parameters.
305 *	Returns: none.
306 */
307
308/* ARGSUSED */
309static void
310Page(Widget w, XEvent * event, String * params, Cardinal * num_params)
311{
312    ScrollByLineWidget sblw = (ScrollByLineWidget) w;
313    Widget bar = sblw->scroll.bar;
314
315    if (*num_params < 1)
316        return;
317/*
318 * If no scroll bar is visible then do not page, as the entire window is shown,
319 * or scrolling has been turned off.
320 */
321
322    if (bar == (Widget) NULL)
323        return;
324
325    switch (params[0][0]) {
326    case 'f':
327    case 'F':
328        /* move one page forward */
329        VerticalScroll(bar, NULL, (XtPointer) ((long) bar->core.height));
330        break;
331    case 'b':
332    case 'B':
333        /* move one page backward */
334        VerticalScroll(bar, NULL, (XtPointer) (-(long) bar->core.height));
335        break;
336    case 'L':
337    case 'l':
338        /* move one line forward */
339        VerticalScroll(bar, NULL,
340                       (XtPointer) ((long) atoi(params[1]) *
341                                    sblw->scroll.font_height));
342        break;
343    default:
344        return;
345    }
346}
347
348/*	Function Name: CreateScrollbar
349 *	Description: createst the scrollbar for us.
350 *	Arguments: w - sblw widget.
351 *	Returns: none.
352 */
353
354static void
355CreateScrollbar(Widget w)
356{
357    ScrollByLineWidget sblw = (ScrollByLineWidget) w;
358    Arg args[5];
359    Cardinal num_args = 0;
360
361    if (sblw->scroll.bar != NULL)
362        return;
363
364    XtSetArg(args[num_args], XtNorientation, XtorientVertical);
365    num_args++;
366
367    sblw->scroll.bar = XtCreateWidget("scrollbar", scrollbarWidgetClass, w,
368                                      args, num_args);
369    XtAddCallback(sblw->scroll.bar, XtNscrollProc, VerticalScroll, NULL);
370    XtAddCallback(sblw->scroll.bar, XtNjumpProc, VerticalJump, NULL);
371}
372
373/*	Function Name: ScrollVerticalText
374 *	Description: This accomplished the actual movement of the text.
375 *	Arguments: w - the ScrollByLine Widget.
376 *                 new_line - the new location for the line pointer
377 *                 force_redisplay - should we force this window to get
378 *                                   redisplayed?
379 *	Returns: True if the thumb needs to be moved.
380 */
381
382static Boolean
383ScrollVerticalText(Widget w, int new_line, Boolean force_redisp)
384{
385    ScrollByLineWidget sblw = (ScrollByLineWidget) w;
386    int num_lines = sblw->scroll.num_visible_lines;
387    int max_lines, old_line;
388    Boolean move_thumb = FALSE;
389
390/*
391 * Do not let the window extend out of bounds.
392 */
393
394    if (new_line < 0) {
395        new_line = 0;
396        move_thumb = TRUE;
397    }
398    else {
399        max_lines = sblw->scroll.lines -
400            (int) w->core.height / sblw->scroll.font_height;
401        if (max_lines < 0)
402            max_lines = 0;
403
404        if (new_line > max_lines) {
405            new_line = max_lines;
406            move_thumb = TRUE;
407        }
408    }
409
410/*
411 * If forced to redisplay then do a full redisplay and return.
412 */
413
414    old_line = sblw->scroll.line_pointer;
415    sblw->scroll.line_pointer = new_line;       /* Set current top of page. */
416
417    if (force_redisp)
418        MoveAndClearText(w, 0, /* cause a full redisplay */ 0, 0);
419
420    if (new_line == old_line)
421        return (move_thumb);
422
423/*
424 * Scroll forward.
425 */
426
427    else if (new_line < old_line) {
428        int lines_to_scroll = old_line - new_line;
429        MoveAndClearText(w, 0, num_lines - lines_to_scroll, lines_to_scroll);
430    }
431
432/*
433 * Scroll back.
434 */
435
436    else {
437        int lines_to_scroll = new_line - old_line;
438        MoveAndClearText(w, lines_to_scroll, num_lines - lines_to_scroll, 0);
439    }
440
441    return (move_thumb);
442}
443
444/*	Function Name: MoveAndClearText
445 *	Description: Blits as much text as it can and clear the
446 *                   remaining area with generate exposures TRUE.
447 *	Arguments: w - the sbl widget.
448 *                 old_y - the old y position.
449 *                 height - height of area to move.
450 *                 new_y - new y position.
451 *	Returns: none
452 */
453
454static void
455MoveAndClearText(Widget w, int old_y, int height, int new_y)
456{
457    ScrollByLineWidget sblw = (ScrollByLineWidget) w;
458    int from_left = sblw->scroll.indent + sblw->scroll.offset - 1;
459    int y_clear;
460
461    old_y *= sblw->scroll.font_height;
462    new_y *= sblw->scroll.font_height;
463    height *= sblw->scroll.font_height;
464
465/*
466 * If we are already at the right location then do nothing.
467 * (height == 0).
468 *
469 * If we have scrolled more than a screen height then just clear
470 * the window.
471 */
472
473    if (height <= sblw->scroll.font_height) {   /* avoid rounding errors. */
474        XClearArea(XtDisplay(w), XtWindow(w), from_left, 0,
475                   (unsigned int) 0, (unsigned int) 0, FALSE);
476        PaintText(w, 0, (int) sblw->core.height);
477        return;
478    }
479
480    if ((int) height + (int) old_y > (int) w->core.height)
481        height = w->core.height - old_y;
482
483    XCopyArea(XtDisplay(w), XtWindow(w), XtWindow(w), sblw->scroll.move_gc,
484              from_left, old_y,
485              (unsigned int) w->core.width - from_left, (unsigned int) height,
486              from_left, new_y);
487
488    if (old_y > new_y)
489        height -= sblw->scroll.font_height / 2; /* clear 1/2 font of extra space,
490                                                   to make sure we don't lose or
491                                                   gain descenders. */
492    else
493        height -= sblw->scroll.font_height;     /* clear 1 font of extra space,
494                                                   to make sure we don't overwrite
495                                                   with a last line in buffer. */
496
497    if (old_y > new_y)
498        y_clear = height;
499    else
500        y_clear = 0;
501
502/*
503 * We cannot use generate exposures, since that may allow another move and
504 * clear before the area get repainted, this would be bad.
505 */
506
507    XClearArea(XtDisplay(w), XtWindow(w), from_left, y_clear,
508               (unsigned int) 0, (unsigned int) (w->core.height - height),
509               FALSE);
510    PaintText(w, (int) y_clear, (int) (w->core.height - height));
511}
512
513/*	Function Name: SetThumbHeight
514 *	Description: Set the height of the thumb.
515 *	Arguments: w - the sblw widget.
516 *	Returns: none
517 */
518
519static void
520SetThumbHeight(Widget w)
521{
522    ScrollByLineWidget sblw = (ScrollByLineWidget) w;
523    float shown;
524
525    if (sblw->scroll.bar == NULL)
526        return;
527
528    if (sblw->scroll.lines == 0)
529        shown = 1.0;
530    else
531        shown = (float) w->core.height / (float) (sblw->scroll.lines *
532                                                  sblw->scroll.font_height);
533    if (shown > 1.0)
534        shown = 1.0;
535
536    XawScrollbarSetThumb(sblw->scroll.bar, (float) -1, shown);
537}
538
539/*	Function Name: SetThumb
540 *	Description: Set the thumb location.
541 *	Arguments: w - the sblw.
542 *	Returns: none
543 */
544
545static void
546SetThumb(Widget w)
547{
548    float location;
549    ScrollByLineWidget sblw = (ScrollByLineWidget) w;
550
551    if ((sblw->scroll.lines == 0) || (sblw->scroll.bar == NULL))
552        return;
553
554    location = (float) sblw->scroll.line_pointer / (float) sblw->scroll.lines;
555    XawScrollbarSetThumb(sblw->scroll.bar, location, (float) -1);
556}
557
558/*	Function Name: VerticalJump.
559 *	Description: This function moves the test
560 *                   as the vertical scroll bar is moved.
561 *	Arguments: w - the scrollbar widget.
562 *                 junk - not used.
563 *                 percent - the position of the scrollbar.
564 *	Returns: none.
565 */
566
567/* ARGSUSED */
568static void
569VerticalJump(Widget w, XtPointer junk, XtPointer percent_ptr)
570{
571    float percent = *((float *) percent_ptr);
572    int new_line;               /* The new location for the line pointer. */
573    ScrollByLineWidget sblw = (ScrollByLineWidget) XtParent(w);
574
575    new_line = (int) ((float) sblw->scroll.lines * percent);
576    if (ScrollVerticalText((Widget) sblw, new_line, FALSE))
577        SetThumb((Widget) sblw);
578}
579
580/*	Function Name: VerticalScroll
581 *	Description: This function moves the position of the interior window
582 *                   as the vertical scroll bar is moved.
583 *	Arguments: w - the scrollbar widget.
584 *                 junk - not used.
585 *                 pos - the position of the cursor.
586 *	Returns: none.
587 */
588
589/* ARGSUSED */
590static void
591VerticalScroll(Widget w, XtPointer client_data, XtPointer call_data)
592{
593    int pos = (int) (long) call_data;
594    int new_line;               /* The new location for the line pointer. */
595    ScrollByLineWidget sblw = (ScrollByLineWidget) XtParent(w);
596
597    new_line = sblw->scroll.line_pointer + (pos / sblw->scroll.font_height);
598    (void) ScrollVerticalText((Widget) sblw, new_line, FALSE);
599    SetThumb((Widget) sblw);
600}
601
602/* ARGSUSED */
603static void
604Initialize(Widget req, Widget new, ArgList args, Cardinal * num_args)
605{
606    ScrollByLineWidget sblw = (ScrollByLineWidget) new;
607    unsigned long figWidth;
608    Atom atomNum;
609
610    sblw->scroll.top_line = NULL;
611    sblw->scroll.line_pointer = 0;
612    LoadFile(new);
613    sblw->scroll.bar = (Widget) NULL;
614
615    sblw->scroll.font_height = (sblw->scroll.normal_font->max_bounds.ascent +
616                                sblw->scroll.normal_font->max_bounds.descent);
617
618    atomNum = XInternAtom(XtDisplay(req), "FIGURE_WIDTH", False);
619
620    if (XGetFontProperty(sblw->scroll.normal_font, atomNum, &figWidth))
621        sblw->scroll.h_width = figWidth;
622    else
623        sblw->scroll.h_width = XTextWidth(sblw->scroll.normal_font, "$", 1);
624}                               /* Initialize. */
625
626/*	Function Name: CreateGCs
627 *	Description: Creates the graphics contexts that we need.
628 *	Arguments: w - the sblw.
629 *	Returns: none
630 */
631
632static void
633CreateGCs(Widget w)
634{
635    ScrollByLineWidget sblw = (ScrollByLineWidget) w;
636
637    XtGCMask mask;
638    XGCValues values;
639
640    values.graphics_exposures = TRUE;
641    sblw->scroll.move_gc = XtGetGC(w, GCGraphicsExposures, &values);
642
643    mask = GCForeground | GCFont;
644    values.foreground = sblw->scroll.foreground;
645
646    values.font = sblw->scroll.normal_font->fid;
647    sblw->scroll.normal_gc = XtGetGC(w, mask, &values);
648
649    values.font = sblw->scroll.italic_font->fid;
650    sblw->scroll.italic_gc = XtGetGC(w, mask, &values);
651
652    values.font = sblw->scroll.bold_font->fid;
653    sblw->scroll.bold_gc = XtGetGC(w, mask, &values);
654
655    values.font = sblw->scroll.symbol_font->fid;
656    sblw->scroll.symbol_gc = XtGetGC(w, mask, &values);
657}
658
659/*	Function Name: DestroyGCs
660 *	Description: removes all gcs for this widget.
661 *	Arguments: w - the widget.
662 *	Returns: none
663 */
664
665static void
666DestroyGCs(Widget w)
667{
668    ScrollByLineWidget sblw = (ScrollByLineWidget) w;
669
670    XtReleaseGC(w, sblw->scroll.normal_gc);
671    XtReleaseGC(w, sblw->scroll.bold_gc);
672    XtReleaseGC(w, sblw->scroll.italic_gc);
673    XtReleaseGC(w, sblw->scroll.move_gc);
674}
675
676static void
677Realize(Widget w, Mask *valueMask, XSetWindowAttributes * attributes)
678{
679    ScrollByLineWidget sblw = (ScrollByLineWidget) w;
680
681    CreateScrollbar(w);
682    CreateGCs(w);
683    Layout(w);
684    (*SuperClass->core_class.realize) (w, valueMask, attributes);
685    XtRealizeWidget(sblw->scroll.bar);  /* realize scrollbar. */
686    XtMapWidget(sblw->scroll.bar);      /* map scrollbar. */
687
688    XtAddEventHandler(w, 0, TRUE, GExpose, NULL);       /* Get Graphics Exposures */
689}                               /* Realize */
690
691/*	Function Name: Destroy
692 *	Description: Cleans up when we are killed.
693 *	Arguments: w - the widget.
694 *	Returns: none
695 */
696
697static void
698Destroy(Widget w)
699{
700    ScrollByLineWidget sblw = (ScrollByLineWidget) w;
701
702    if (sblw->scroll.bar != NULL)
703        XtDestroyWidget(sblw->scroll.bar);      /* Destroy scrollbar. */
704    if (sblw->scroll.file != NULL) {
705        fclose(sblw->scroll.file);
706        sblw->scroll.file = NULL;
707    }
708    LoadFile(w);
709    DestroyGCs(w);
710}
711
712/*
713 *
714 * Set Values
715 *
716 */
717
718/* ARGSUSED */
719static Boolean
720SetValuesHook(Widget w, ArgList args, Cardinal * num_args)
721{
722    Boolean ret = TRUE;
723    Cardinal i;
724
725    for (i = 0; i < *num_args; i++) {
726        if (strcmp(XtNfile, args[i].name) == 0) {
727            LoadFile(w);
728            ret = TRUE;
729        }
730    }
731
732/*
733 * Changing anything else will have strange effects, I don't need it so
734 * I didn't code it.
735 */
736
737    return (ret);
738
739}                               /* Set Values */
740
741/*
742 * A little design philosophy is probably wise to include at this point.
743 *
744 * One of the things that I has hoped to achieve with xman is to make the
745 * viewing of manpage not only easy for the nieve user, but also fast for
746 * the experienced user, I wanted to be able to use it too.  To achieve this
747 * I end up sacrificing a bit of start up time for the manual data structure.
748 * As well as, the overhead of reading the entire file before putting it up
749 * on the display.  This is actually hardly even noticeable since most manual
750 * pages are shots, one to two pages - the notable exception is of course csh,
751 * but then that should be broken up anyway.
752 *
753 * METHOD:
754 *
755 * I allocate a chunk of space that is the size of the file, plus a null for
756 * debugging.  Then copies the file into this chunk of memory. I then allocate
757 * an array of char*'s that are assigned to the beginning of each line.  Yes,
758 * this means that I have to read the file twice, and could probably be more
759 * clever about it, but once it is in memory the second read is damn fast.
760 * There are a few obscurities here about guessing the number of lines and
761 * reallocing if I guess wrong, but other than that it is pretty straight
762 * forward.
763 *
764 *                                         Chris Peterson
765 *                                         1/27/88
766 */
767
768#define ADD_MORE_MEM 100        /* first guesses for allocations. */
769#define CHAR_PER_LINE 40
770
771/*	Function Name: LoadFile
772 *	Description: Loads the current file into the internal memory.
773 *	Arguments: w - the sblw.
774 *	Returns: none
775 */
776
777static void
778LoadFile(Widget w)
779{
780    ScrollByLineWidget sblw = (ScrollByLineWidget) w;
781    FILE *file = sblw->scroll.file;
782
783    char *page;
784    char **line_pointer, **top_line;    /* pointers to beginnings of the
785                                           lines of the file. */
786    int nlines;                 /* the current number of allocated lines. */
787    struct stat fileinfo;       /* file information from fstat. */
788
789    if (sblw->scroll.top_line != NULL) {
790        XtFree(*(sblw->scroll.top_line));       /* free characters. */
791        XtFree((char *) (sblw->scroll.top_line));       /* free lines. */
792    }
793    sblw->scroll.top_line = NULL;
794
795    if (file == NULL)
796        return;
797
798/*
799 * Get file size and allocate a chunk of memory for the file to be
800 * copied into.
801 */
802
803    if (fstat(fileno(file), &fileinfo))
804        XtAppError(XtWidgetToApplicationContext(w),
805                   "SBLW LoadFile: Failure in fstat.");
806
807/*
808 * The XtMalloc below is limited to a size of int by the libXt API.
809 */
810    if (fileinfo.st_size >= INT_MAX)
811        XtAppError(XtWidgetToApplicationContext(w),
812                   "SBLW LoadFile: File too large.");
813
814/*
815 * Allocate a space for a list of pointer to the beginning of each line.
816 */
817
818    if ((nlines = fileinfo.st_size / CHAR_PER_LINE) == 0)
819        return;
820
821    page = XtMalloc(fileinfo.st_size + 1);      /* leave space for the NULL */
822    top_line = line_pointer = (char **) XtMalloc(nlines * sizeof(char *));
823
824/*
825 * Copy the file into memory.
826 */
827
828    fseek(file, 0L, SEEK_SET);
829    if (fread(page, sizeof(char), fileinfo.st_size, file) == 0)
830        XtAppError(XtWidgetToApplicationContext(w),
831                   "SBLW LoadFile: Failure in fread.");
832
833/* put NULL at end of buffer. */
834
835    *(page + fileinfo.st_size) = '\0';
836
837/*
838 * Go through the file setting a line pointer to the character after each
839 * new line.  If we run out of line pointer space then realloc that space
840 * with space for more lines.
841 */
842
843    *line_pointer++ = page;     /* first line points to first char in buffer. */
844    while (*page != '\0') {
845
846        if (*page == '\n') {
847            *line_pointer++ = page + 1;
848
849            if (line_pointer >= top_line + nlines) {
850                top_line = (char **) XtRealloc((char *) top_line, (nlines +
851                                                                   ADD_MORE_MEM)
852                                               * sizeof(char *));
853                line_pointer = top_line + nlines;
854                nlines += ADD_MORE_MEM;
855            }
856        }
857        page++;
858    }
859
860/*
861 *  Realloc the line pointer space to take only the minimum amount of memory
862 */
863
864    sblw->scroll.lines = nlines = line_pointer - top_line - 1;
865    top_line = (char **) XtRealloc((char *) top_line, nlines * sizeof(char *));
866
867/*
868 * Store the memory pointers
869 */
870
871    sblw->scroll.top_line = top_line;
872    sblw->scroll.line_pointer = 0;
873    SetThumbHeight(w);
874    SetThumb(w);
875}
876
877#define NLINES  66              /* This is the number of lines to wait until
878                                   we boldify the line again, this allows
879                                   me to bold the first line of each page. */
880#define BACKSPACE 010           /* I doubt you would want to change this. */
881
882#define NORMAL	0
883#define BOLD	1
884#define ITALIC	2
885#define SYMBOL	3
886#define WHICH(italic, bold)	((bold) ? BOLD : ((italic) ? ITALIC : NORMAL))
887                                /* Choose BOLD over ITALICS.  If neither */
888                                /* is chosen, use NORMAL. */
889
890static int DumpText(Widget w, int x_loc, int y_loc, char *buf, int len,
891                    int format);
892static Boolean Boldify(char *);
893
894/*	Function Name: PrintText
895 *	Description: This function actually prints the text.
896 *	Arguments: w - the ScrollByLine widget.
897 *                 start_line - line to start printing,
898 *                 num_lines - the number of lines to print.
899 *                 location - the location to print the text.
900 *	Returns: none.
901 */
902
903/* ARGSUSED */
904static void
905PrintText(Widget w, int start_line, int num_lines, int location)
906{
907    ScrollByLineWidget sblw = (ScrollByLineWidget) w;
908
909    register char *bufp, *c;    /* Generic char pointers */
910    int current_line;           /* the number of the current line */
911    char buf[BUFSIZ];           /* Misc. characters */
912    Boolean italicflag = FALSE; /* Print text in italics?? */
913    Boolean first = TRUE;       /* First line of a manual page??? */
914    int x_loc, y_loc;           /* x and y location of text. */
915
916/*
917 * For table hack
918 * To get columns to line up reasonably in most cases, make the
919 * assumption that they were lined up using lots of spaces, where
920 * lots is greater than two. Use a space width of 70% of the
921 * widest character in the font.
922 */
923    int h_col, h_fix;
924    char *h_c;
925
926/*
927 * Nothing loaded, take no action.
928 */
929
930    if (sblw->scroll.top_line == NULL || num_lines == 0)
931        return;
932
933    current_line = start_line;
934
935/* Set the first character to print at the first line. */
936
937    c = *(sblw->scroll.top_line + start_line);
938
939/*
940 * Because XDrawString uses the bottom of the text as a position
941 * reference, add the height from the top of the font to the baseline
942 * to the ScollByLine position reference.
943 */
944
945    y_loc = location + sblw->scroll.normal_font->max_bounds.ascent;
946
947/*
948 * Ok, here's the more than mildly heuristic man page formatter.
949 * We put chars into buf until either a font change or newline
950 * occurs (at which time we flush it to the screen.)
951 */
952
953    bufp = buf;
954    x_loc = sblw->scroll.offset + sblw->scroll.indent;
955    h_col = 0;
956
957/*
958 * A fix:
959 * Assume that we are always starting to print on or before the
960 * first line of a page, and then prove it if we aren't.
961 */
962    for (h_fix = 1; h_fix <= (start_line % NLINES); h_fix++)
963        if (**(sblw->scroll.top_line + start_line - h_fix) != '\n') {
964            first = FALSE;
965            break;
966        }
967
968    while (TRUE) {
969        if (current_line % NLINES == 0)
970            first = TRUE;
971
972/*
973 * Lets make sure that we do not run out of space in our buffer, making full
974 * use of it is not critical since no window will be wide enough to display
975 * nearly BUFSIZ characters.
976 */
977
978        if ((bufp - buf) > (BUFSIZ - 10))
979            /* Toss everything until we find a <CR> or the end of the buffer. */
980            while ((*c != '\n') && (*c != '\0'))
981                c++;
982
983        switch (*c) {
984
985        case '\0':             /* If we reach the end of the file then return */
986            DumpText(w, x_loc, y_loc, buf, bufp - buf,
987                     WHICH(italicflag, first));
988            return;
989
990        case '\n':
991            if (bufp != buf) {
992                Boolean bold;
993
994                *bufp = '\0';   /* for Boldify. */
995                bold = ((first) || ((x_loc == (sblw->scroll.offset +
996                                               sblw->scroll.indent)) &&
997                                    Boldify(buf)));
998
999                (void) DumpText(w, x_loc, y_loc, buf, bufp - buf,
1000                                WHICH(italicflag, bold));
1001
1002                if (bold)
1003                    first = FALSE;
1004            }
1005
1006/*
1007 * If we have painted the required number of lines then we should now return.
1008 */
1009            if (++current_line == start_line + num_lines)
1010                return;
1011
1012            bufp = buf;
1013            italicflag = FALSE;
1014            x_loc = sblw->scroll.offset + sblw->scroll.indent;
1015            h_col = 0;
1016            y_loc += sblw->scroll.font_height;
1017            break;
1018
1019/*
1020 * This tab handling code is not very clever it moves the cursor over
1021 * to the next boundary of eight (8) spaces, as calculated in width just
1022 * before the printing loop started.
1023 */
1024
1025        case '\t':             /* TAB */
1026            x_loc = DumpText(w, x_loc, y_loc, buf, bufp - buf,
1027                             WHICH(italicflag, first));
1028            h_col += bufp - buf;
1029            bufp = buf;
1030            italicflag = FALSE;
1031            x_loc = sblw->scroll.offset + sblw->scroll.indent;
1032            h_col = h_col + 8 - (h_col % 8);
1033            x_loc += sblw->scroll.h_width * h_col;
1034            break;
1035
1036        case ' ':
1037            h_c = c + 1;
1038            while (*h_c == ' ')
1039                h_c++;
1040
1041            if (h_c - c < 4) {
1042                *bufp++ = *c;
1043                break;
1044            }
1045
1046            x_loc = DumpText(w, x_loc, y_loc, buf, bufp - buf,
1047                             WHICH(italicflag, first));
1048            h_col += bufp - buf;
1049            bufp = buf;
1050            italicflag = FALSE;
1051
1052            x_loc = sblw->scroll.offset + sblw->scroll.indent;
1053            h_col += (h_c - c);
1054            x_loc += sblw->scroll.h_width * h_col;
1055            c = h_c - 1;
1056            break;
1057
1058        case '\033':           /* ignore esc sequences for now */
1059            c++;                /* should always be esc-x */
1060            break;
1061
1062/*
1063 * Overstrike code supplied by: cs.utexas.edu!ut-emx!clyde@rutgers.edu
1064 * Since my manual pages do not have overstrike I couldn't test this.
1065 */
1066
1067        case BACKSPACE:        /* Backspacing for nroff bolding */
1068            if (c[-1] == c[1] && c[1] != BACKSPACE) {   /* overstriking one char */
1069                if (bufp > buf) {
1070                    bufp--;     /* Zap 1st instance of char to bolden */
1071                    x_loc = DumpText(w, x_loc, y_loc, buf, bufp - buf,
1072                                     WHICH(italicflag, FALSE));
1073                    h_col += bufp - buf;
1074                }
1075                bufp = buf;
1076                *bufp++ = c[1];
1077                x_loc = DumpText(w, x_loc, y_loc, buf, bufp - buf, BOLD);
1078                h_col += bufp - buf;
1079                bufp = buf;
1080                first = FALSE;
1081
1082                /*
1083                 *     Nroff bolding looks like:
1084                 *               C\bC\bC\bCN...
1085                 * c points to ----^      ^
1086                 * it needs to point to --^
1087                 */
1088                while (*c == BACKSPACE && c[-1] == c[1])
1089                    c += 2;
1090                c--;            /* Back up to previous char */
1091            }
1092            else {
1093                if ((c[-1] == 'o' && c[1] == '+')       /* Nroff bullet */
1094                    ||(c[-1] == '+' && c[1] == 'o')) {  /* Nroff bullet */
1095                    /* If we run into a bullet, print out */
1096                    /* everything that's accumulated to this */
1097                    /* point, then the bullet, then resume. */
1098                    if (bufp > buf) {
1099                        bufp--;
1100                        x_loc = DumpText(w, x_loc, y_loc, buf, bufp - buf,
1101                                         WHICH(italicflag, FALSE));
1102                        h_col += bufp - buf;
1103                    }
1104                    bufp = buf;
1105                    *bufp = (char) 183;
1106                    x_loc = DumpText(w, x_loc, y_loc, buf, 1, SYMBOL);
1107                    h_col++;
1108                    c++;
1109                }
1110                else {          /* 'real' backspace - back up output ptr */
1111                    if (bufp > buf)
1112                        bufp--;
1113                }
1114            }
1115            break;
1116
1117/* End of contributed overstrike code. */
1118
1119        case '_':              /* look for underlining [italicize] */
1120            if (*(c + 1) == BACKSPACE) {
1121                if (!italicflag) {      /* font change? */
1122                    if (bufp != buf) {
1123                        x_loc =
1124                            DumpText(w, x_loc, y_loc, buf, bufp - buf, NORMAL);
1125                        h_col += bufp - buf;
1126                        bufp = buf;
1127                    }
1128                    italicflag = TRUE;
1129                }
1130                c += 2;
1131                *bufp++ = *c;
1132                break;
1133            }
1134            /* else fall through - to default, because this was a real underscore. */
1135
1136        default:
1137            if (italicflag) {   /* font change? */
1138                if (bufp != buf) {
1139                    x_loc = DumpText(w, x_loc, y_loc, buf, bufp - buf,
1140                                     WHICH(italicflag, FALSE));
1141                    h_col += bufp - buf;
1142                    bufp = buf;
1143                }
1144                italicflag = FALSE;
1145            }
1146            *bufp++ = *c;
1147            break;
1148        }
1149        c++;
1150    }
1151}
1152
1153/*	Function Name: DumpText
1154 *	Description: Dumps text to the screen.
1155 *	Arguments: w - the widget.
1156 *                 x_loc - to dump text at.
1157 *                 y_loc - the y_location to draw_text.
1158 *                 buf - buffer to dump.
1159 *                 italic, bold, boolean that tells us which gc to use.
1160 *	Returns: x_location of the end of the text.
1161 */
1162
1163static int
1164DumpText(Widget w, int x_loc, int y_loc, char *buf, int len, int format)
1165{
1166    ScrollByLineWidget sblw = (ScrollByLineWidget) w;
1167    GC gc;
1168    XFontStruct *font;
1169
1170    switch (format) {
1171
1172    case ITALIC:
1173        gc = sblw->scroll.italic_gc;
1174        font = sblw->scroll.italic_font;
1175        break;
1176
1177    case BOLD:
1178        gc = sblw->scroll.bold_gc;
1179        font = sblw->scroll.bold_font;
1180        break;
1181
1182    case SYMBOL:
1183        gc = sblw->scroll.symbol_gc;
1184        font = sblw->scroll.symbol_font;
1185        break;
1186
1187    default:
1188        gc = sblw->scroll.normal_gc;
1189        font = sblw->scroll.normal_font;
1190        break;
1191    }
1192
1193    XDrawString(XtDisplay(w), XtWindow(w), gc, x_loc, y_loc, buf, len);
1194    return (x_loc + XTextWidth(font, buf, len));
1195}
1196
1197/*	Function Name: Boldify
1198 *	Description: look for keyword.
1199 *	Arguments: sp - string pointer.
1200 *	Returns: 1 if keyword else 0.
1201 */
1202
1203static Boolean
1204Boldify(register char *sp)
1205{
1206    register char *sp_pointer;
1207    int length, count;
1208
1209/*
1210 * If there are not lower case letters in the line the assume it is a
1211 * keyword and boldify it in PrintManpage.
1212 */
1213
1214    length = strlen(sp);
1215    for (sp_pointer = sp, count = 0; count < length; sp_pointer++, count++)
1216        if (!isupper(*sp_pointer) && !isspace(*sp_pointer))
1217            return (0);
1218    return (1);
1219}
1220
1221#undef superclass
1222#undef SuperClass
1223