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