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