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