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