Annotation of researchv9/X11/src/X.V11R1/lib/Xtk/Scroll.c, revision 1.1.1.1

1.1       root        1: #ifndef lint
                      2: static char rcsid[] = "$Header: Scroll.c,v 1.7 87/09/14 00:43:28 swick Exp $";
                      3: #endif lint
                      4: 
                      5: /*
                      6:  * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
                      7:  * 
                      8:  *                         All Rights Reserved
                      9:  * 
                     10:  * Permission to use, copy, modify, and distribute this software and its 
                     11:  * documentation for any purpose and without fee is hereby granted, 
                     12:  * provided that the above copyright notice appear in all copies and that
                     13:  * both that copyright notice and this permission notice appear in 
                     14:  * supporting documentation, and that the name of Digital Equipment
                     15:  * Corporation not be used in advertising or publicity pertaining to
                     16:  * distribution of the software without specific, written prior permission.  
                     17:  * 
                     18:  * 
                     19:  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
                     20:  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
                     21:  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
                     22:  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
                     23:  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
                     24:  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
                     25:  * SOFTWARE.
                     26:  */
                     27: /* ScrollBar.c */
                     28: /* created by weissman, Mon Jul  7 13:20:03 1986 */
                     29: /* converted by swick, Thu Aug 27 1987 */
                     30: 
                     31: #include "Xlib.h"
                     32: #include "Xresource.h"
                     33: /*#include "Conversion.h"*/
                     34: #include "Intrinsic.h"
                     35: #include "Scroll.h"
                     36: #include "ScrollP.h"
                     37: #include "Atoms.h"
                     38: #include "cursorfont.h"
                     39: 
                     40: /* Private definitions. */
                     41: 
                     42: static char *defaultTranslationTable[] = {
                     43:        "<Btn1Down>:            StartPageBack\n", /* should be generic... */
                     44:        "<Btn2Down>:            StartScroll\n",
                     45:        "<Btn3Down>:            StartPageForward\n", /* ...but needs TM args */
                     46:        "<Btn1Up>:              DoPageBack\n",
                     47:        "<Btn2Up>:              DoScroll\n",
                     48:        "<Btn3Up>:              DoPageForward\n",
                     49:        "<Motion>2:             MoveThumb\n", /* bug? in TM forces button spec here */
                     50:        NULL
                     51: };
                     52: 
                     53: /* grodyness needed because Xrm wants pointer to thing, not thing... */
                     54: static caddr_t defaultTranslations = (caddr_t)defaultTranslationTable;
                     55: 
                     56: static XtResource resources[] = {
                     57:   {XtNorientation, XtCOrientation, XtROrientation, sizeof(XtOrientation),
                     58:      XtOffset(ScrollbarWidget, scrollbar.orientation), XtRString, "vertical"},
                     59:   {XtNscrollProc, XtCScrollProc, XtRFunction, sizeof(XtCallbackProc),
                     60:      XtOffset(ScrollbarWidget, scrollbar.scrollProc), XtRFunction, NULL},
                     61:   {XtNthumbProc, XtCScrollProc, XtRFunction, sizeof(XtCallbackProc),
                     62:      XtOffset(ScrollbarWidget, scrollbar.thumbProc), XtRFunction, NULL},
                     63:   {XtNparameter, XtCParameter, XtRPointer, sizeof(caddr_t),
                     64:      XtOffset(ScrollbarWidget, scrollbar.closure), XtRPointer, NULL},
                     65:   {XtNthumb, XtCThumb, XtRPixmap, sizeof(Pixmap),
                     66:      XtOffset(ScrollbarWidget, scrollbar.thumb), XtRPixmap, NULL},
                     67:   {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
                     68:      XtOffset(ScrollbarWidget, scrollbar.foreground), XtRString, "black"},
                     69:   {XtNscrollVCursor, XtCScrollVCursor, XtRCursor, sizeof(Cursor),
                     70:      XtOffset(ScrollbarWidget, scrollbar.verCursor), XtRString, "sb_v_double_arrow"},
                     71:   {XtNscrollHCursor, XtCScrollHCursor, XtRCursor, sizeof(Cursor),
                     72:      XtOffset(ScrollbarWidget, scrollbar.horCursor), XtRString, "sb_h_double_arrow"},
                     73:   {XtNscrollUCursor, XtCScrollUCursor, XtRCursor, sizeof(Cursor),
                     74:      XtOffset(ScrollbarWidget, scrollbar.upCursor), XtRString, "sb_up_arrow"},
                     75:   {XtNscrollDCursor, XtCScrollDCursor, XtRCursor, sizeof(Cursor),
                     76:      XtOffset(ScrollbarWidget, scrollbar.downCursor), XtRString, "sb_down_arrow"},
                     77:   {XtNscrollLCursor, XtCScrollLCursor, XtRCursor, sizeof(Cursor),
                     78:      XtOffset(ScrollbarWidget, scrollbar.leftCursor), XtRString, "sb_left_arrow"},
                     79:   {XtNscrollRCursor, XtCScrollRCursor, XtRCursor, sizeof(Cursor),
                     80:      XtOffset(ScrollbarWidget, scrollbar.rightCursor), XtRString, "sb_right_arrow"},
                     81:   {XtNeventBindings, XtCEventBindings, XtRStringTable, sizeof(_XtTranslations),
                     82:      XtOffset(ScrollbarWidget, core.translations), XtRStringTable, (caddr_t)&defaultTranslations},
                     83: };
                     84: 
                     85: static void ClassInitialize();
                     86: static void Initialize();
                     87: static void Realize();
                     88: static void Resize();
                     89: static void Redisplay();
                     90: static Boolean SetValues();
                     91: 
                     92: static void StartPageBack();
                     93: static void StartSmoothScroll();
                     94: static void StartPageForward();
                     95: static void DoPageBack();
                     96: static void DoSmoothScroll();
                     97: static void DoPageForward();
                     98: static void MoveThumb();
                     99: 
                    100: static XtActionsRec actions[] = {
                    101:        {"StartPageBack",       (caddr_t)StartPageBack},
                    102:        {"StartScroll",         (caddr_t)StartSmoothScroll},
                    103:        {"StartPageForward",    (caddr_t)StartPageForward},
                    104:        {"DoPageBack",          (caddr_t)DoPageBack},
                    105:        {"DoScroll",            (caddr_t)DoSmoothScroll},
                    106:        {"DoPageForward",       (caddr_t)DoPageForward},
                    107:        {"MoveThumb",           (caddr_t)MoveThumb},
                    108:        {NULL,NULL}
                    109: };
                    110: 
                    111: 
                    112: static ScrollbarClassRec scrollbarClassRec = {
                    113: /* core fields */
                    114:     /* superclass       */      (WidgetClass) &widgetClassRec,
                    115:     /* class_name       */      "Scroll",
                    116:     /* size             */      sizeof(ScrollbarRec),
                    117:     /* class_initialize        */      ClassInitialize,
                    118:     /* class_inited    */      FALSE,
                    119:     /* initialize       */      Initialize,
                    120:     /* realize          */      Realize,
                    121:     /* actions          */      actions,
                    122:     /* num_actions     */      XtNumber(actions),
                    123:     /* resources        */      resources,
                    124:     /* num_resources    */      XtNumber(resources),
                    125:     /* xrm_class        */      NULLQUARK,
                    126:     /* compress_motion */      FALSE,
                    127:     /* compress_exposure*/     TRUE,
                    128:     /* visible_interest */      FALSE,
                    129:     /* destroy          */      NULL,
                    130:     /* resize           */      Resize,
                    131:     /* expose           */      Redisplay,
                    132:     /* set_values       */      SetValues,
                    133:     /* accept_focus     */      NULL,
                    134: };
                    135: 
                    136: WidgetClass scrollbarWidgetClass = (WidgetClass)&scrollbarClassRec;
                    137: 
                    138: #define MINBARHEIGHT   7     /* How many pixels of scrollbar to always show */
                    139: #define NoButton -1
                    140: #define PICKLENGTH(widget, x, y) \
                    141:     ((widget->scrollbar.orientation == XtorientHorizontal) ? x : y)
                    142: #define PICKTHICKNESS(widget, x, y) \
                    143:     ((widget->scrollbar.orientation == XtorientHorizontal) ? y : x)
                    144: #define MIN(x,y)       ((x) < (y) ? (x) : (y))
                    145: #define MAX(x,y)       ((x) > (y) ? (x) : (y))
                    146: 
                    147: 
                    148: /* Orientation enumeration constants */
                    149: 
                    150: static XrmQuark  XtQEhorizontal;
                    151: static XrmQuark  XtQEvertical;
                    152: 
                    153: /*ARGSUSED*/
                    154: #define        done(address, type) \
                    155:        { (*toVal).size = sizeof(type); (*toVal).addr = (caddr_t) address; }
                    156: 
                    157: extern void _XLowerCase();
                    158: 
                    159: static void CvtStringToOrientation(dpy, fromVal, toVal)
                    160:     Display    *dpy;
                    161:     XrmValue   fromVal;
                    162:     XrmValue   *toVal;
                    163: {
                    164:     static XtOrientation orient;
                    165:     XrmQuark   q;
                    166:     char       lowerName[1000];
                    167: 
                    168: /* ||| where to put LowerCase */
                    169:     _XLowerCase((char *) fromVal.addr, lowerName);
                    170:     q = XrmAtomToQuark(lowerName);
                    171:     if (q == XtQEhorizontal) {
                    172:        orient = XtorientHorizontal;
                    173:        done(&orient, XtOrientation);
                    174:        return;
                    175:     }
                    176:     if (q == XtQEvertical) {
                    177:        orient = XtorientVertical;
                    178:        done(&orient, XtOrientation);
                    179:        return;
                    180:     }
                    181: };
                    182: 
                    183: 
                    184: static void ClassInitialize()
                    185: {
                    186:     XtQEhorizontal = XrmAtomToQuark(XtEhorizontal);
                    187:     XtQEvertical   = XrmAtomToQuark(XtEvertical);
                    188:     XrmRegisterTypeConverter(XrmRString, XtROrientation, CvtStringToOrientation);
                    189: }
                    190: 
                    191: 
                    192: 
                    193: /*
                    194:  * Make sure the first number is within the range specified by the other
                    195:  * two numbers.
                    196:  */
                    197: 
                    198: static int InRange(num, small, big)
                    199: int num, small, big;
                    200: {
                    201:     return (num < small) ? small : ((num > big) ? big : num);
                    202: }
                    203: 
                    204: /*
                    205:  * Same as above, but for floating numbers. 
                    206:  */
                    207: 
                    208: static float FloatInRange(num, small, big)
                    209: float num, small, big;
                    210: {
                    211:     return (num < small) ? small : ((num > big) ? big : num);
                    212: }
                    213: 
                    214: 
                    215: /* Fill the area specified by top and bottom with the given pattern. */
                    216: static float FractionLoc(w, x, y)
                    217:   ScrollbarWidget w;
                    218:   int x, y;
                    219: {
                    220:     float   result;
                    221: 
                    222:     result = PICKLENGTH(w, (float) x/w->core.width,
                    223:                        (float) y/w->core.height);
                    224:     return FloatInRange(result, 0.0, 1.0);
                    225: }
                    226: 
                    227: 
                    228: static FillArea(w, top, bottom, thumb)
                    229:   ScrollbarWidget w;
                    230:   Position top, bottom;
                    231:   int thumb;
                    232: {
                    233:     Dimension length = bottom-top;
                    234: 
                    235:     switch(thumb) {
                    236:        /* Fill the new Thumb location */
                    237:       case 1:
                    238:        if (w->scrollbar.orientation == XtorientHorizontal) 
                    239:            XFillRectangle(XtDisplay(w), XtWindow(w),
                    240:                           w->scrollbar.gc, top, 1, length,
                    241:                           w->core.height-2);
                    242:        
                    243:        else XFillRectangle(XtDisplay(w), XtWindow(w), w->scrollbar.gc,
                    244:                            1, top, w->core.width-2, length);
                    245: 
                    246:        break;
                    247:        /* Clear the old Thumb location */
                    248:       case 0:
                    249:        if (w->scrollbar.orientation == XtorientHorizontal) 
                    250:            XClearArea(XtDisplay(w), XtWindow(w), top, 1,
                    251:                       length, w->core.height-2, FALSE);
                    252:        
                    253:        else XClearArea(XtDisplay(w), XtWindow(w), 1,
                    254:                        top, w->core.width-2, length, FALSE);
                    255: 
                    256:     }  
                    257: }
                    258: 
                    259: 
                    260: /* Paint the thumb in the area specified by w->top and
                    261:    w->shown.  The old area is erased.  The painting and
                    262:    erasing is done cleverly so that no flickering will occur. */
                    263: 
                    264: static void PaintThumb( w )
                    265:   ScrollbarWidget w;
                    266: {
                    267:     int length, oldtop, oldbot, newtop, newbot;
                    268:     length = PICKLENGTH(w, w->core.width, w->core.height);
                    269:     oldtop = w->scrollbar.topLoc;
                    270:     oldbot = oldtop + w->scrollbar.shownLength;
                    271:     newtop = length * w->scrollbar.top;
                    272:     newbot = newtop + length * (w->scrollbar.shown);
                    273:     if (newbot < newtop + MINBARHEIGHT) newbot = newtop + MINBARHEIGHT;
                    274:     if (newtop < oldtop)
                    275:        FillArea(w, newtop, MIN(newbot, oldtop), 1);
                    276:     if (newtop > oldtop)
                    277:        FillArea(w, oldtop, MIN(newtop, oldbot), 0);
                    278:     if (newbot < oldbot)
                    279:        FillArea(w, MAX(newbot, oldtop), oldbot, 0);
                    280:     if (newbot > oldbot)
                    281:        FillArea(w, MAX(newtop, oldbot), newbot, 1);
                    282:     w->scrollbar.topLoc = newtop;
                    283:     w->scrollbar.shownLength = newbot - newtop;
                    284: }
                    285: 
                    286: 
                    287: static void Initialize( request, new )
                    288:    Widget request;             /* what the client asked for */
                    289:    Widget new;                 /* what we're going to give him */
                    290: {
                    291:     ScrollbarWidget w = (ScrollbarWidget) new;
                    292:     XGCValues gcValues;
                    293: 
                    294:     if (w->scrollbar.thumb == NULL) {
                    295:         w->scrollbar.thumb = XtGrayPixmap( XtScreen(w) );
                    296:     }
                    297: 
                    298:     gcValues.fill_style = FillTiled;
                    299:     gcValues.tile = w->scrollbar.thumb;
                    300:     w->scrollbar.gc = XtGetGC( w, GCFillStyle | GCTile, &gcValues);
                    301: 
                    302:     if (w->core.width == 0)  w->core.width = 1;
                    303:     if (w->core.height == 0) w->core.height = 1;
                    304: 
                    305: }
                    306: 
                    307: static void Realize( gw, valueMask, attributes )
                    308:    Widget gw;
                    309:    Mask valueMask;
                    310:    XSetWindowAttributes *attributes;
                    311: {
                    312:     ScrollbarWidget w = (ScrollbarWidget) gw;
                    313:     XSetWindowAttributes winAttr;
                    314:     Mask attrMask;
                    315: 
                    316:     w->scrollbar.inactiveCursor =
                    317:       (w->scrollbar.orientation == XtorientVertical)
                    318:        ? w->scrollbar.verCursor
                    319:        : w->scrollbar.horCursor;
                    320: 
                    321:     winAttr = *attributes;
                    322:     winAttr.cursor = w->scrollbar.inactiveCursor;
                    323:     attrMask = valueMask | CWCursor;
                    324:     
                    325:     w->core.window =
                    326:       XCreateWindow(
                    327:                     XtDisplay( w ), XtWindow(w->core.parent),
                    328:                     w->core.x, w->core.y,
                    329:                     w->core.width, w->core.height,
                    330:                     w->core.border_width, w->core.depth,
                    331:                     InputOutput, (Visual *)CopyFromParent,
                    332:                     attrMask, &winAttr );
                    333: }
                    334: 
                    335: 
                    336: static Boolean SetValues( current, request, desired )
                    337:    Widget current,             /* what I am */
                    338:           request,             /* what he wants me to be */
                    339:           desired;             /* what I will become */
                    340: {
                    341:     ScrollbarWidget w = (ScrollbarWidget) current;
                    342:     ScrollbarWidget rw = (ScrollbarWidget) request;
                    343:     ScrollbarWidget dw = (ScrollbarWidget) desired;
                    344:     short thumbmoved, redraw;
                    345: 
                    346:     thumbmoved = redraw = FALSE;
                    347: 
                    348:     /* Core make take care of the following... we'll have to see */
                    349:     if (w->core.border_pixel != dw->core.border_pixel) {
                    350:        if (w->core.border_width != 0)
                    351:            XSetWindowBorder( XtDisplay(w), XtWindow(w), w->core.border_pixel );
                    352:     }
                    353: 
                    354:     if (w->scrollbar.foreground != rw->scrollbar.foreground ||
                    355:        w->core.background_pixel != rw->core.background_pixel)
                    356:         redraw = TRUE;
                    357: 
                    358:     if (w->scrollbar.top != dw->scrollbar.top ||
                    359:         w->scrollbar.shown != dw->scrollbar.shown)
                    360:        thumbmoved = TRUE;
                    361: 
                    362:     if (redraw)
                    363:        w->scrollbar.topLoc = -1000;
                    364: 
                    365:     return( redraw || thumbmoved );
                    366: }
                    367: 
                    368: /* ARGSUSED */
                    369: static void Resize( gw, geometry )
                    370:    Widget gw;
                    371:    XtWidgetGeometry geometry;
                    372: {
                    373:     ScrollbarWidget w = (ScrollbarWidget) gw;
                    374: 
                    375:     FillArea( w, w->scrollbar.topLoc, 
                    376:              w->scrollbar.topLoc + w->scrollbar.shownLength, 0 );
                    377: 
                    378:     w->scrollbar.topLoc = -1000; /* Forces entire thumb to be painted. */
                    379:     PaintThumb( w );
                    380:     
                    381: }
                    382: 
                    383: 
                    384: /* ARGSUSED */
                    385: static void Redisplay( gw, event )
                    386:    Widget gw;
                    387:    XEvent *event;
                    388: {
                    389:     ScrollbarWidget w = (ScrollbarWidget) gw;
                    390: 
                    391:     w->scrollbar.topLoc = -1000; /* Forces entire thumb to be painted. */
                    392:     PaintThumb( w ); 
                    393: }
                    394: 
                    395: 
                    396: static void StartScroll(gw, event, direction)
                    397:   Widget gw;
                    398:   XEvent *event;
                    399:   char direction;              /* Back|Forward|Smooth */
                    400: {
                    401:     ScrollbarWidget w = (ScrollbarWidget) gw;
                    402:     Cursor cursor;
                    403: 
                    404:     if (w->scrollbar.direction != 0) return;
                    405:     w->scrollbar.direction = direction;
                    406: 
                    407:     switch( direction ) {
                    408:        case 'B':
                    409:        case 'b':       cursor = (w->scrollbar.orientation == XtorientVertical)
                    410:                                   ? w->scrollbar.upCursor
                    411:                                   : w->scrollbar.leftCursor; break;
                    412: 
                    413:        case 'F':
                    414:        case 'f':       cursor = (w->scrollbar.orientation == XtorientVertical)
                    415:                                   ? w->scrollbar.downCursor
                    416:                                   : w->scrollbar.rightCursor; break;
                    417: 
                    418:        case 'S':
                    419:        case 's':       cursor = (w->scrollbar.orientation == XtorientVertical)
                    420:                                   ? w->scrollbar.rightCursor
                    421:                                   : w->scrollbar.upCursor; break;
                    422: 
                    423:        default:        return; /* invalid invocation */
                    424:     }
                    425: 
                    426:     XDefineCursor(XtDisplay(w), XtWindow(w), cursor);
                    427: 
                    428:     XFlush(XtDisplay(w));
                    429: 
                    430:     if (direction == 'S' || direction == 's') MoveThumb(gw, event);
                    431: 
                    432: }
                    433: 
                    434: 
                    435: /* The following are only needed until the TM implements args */
                    436: 
                    437: static void StartPageBack( gw, event )
                    438:    Widget gw;
                    439:    XEvent *event;
                    440: {
                    441:     StartScroll( gw, event, 'B' );
                    442: };
                    443: 
                    444: static void StartPageForward( gw, event )
                    445:    Widget gw;
                    446:    XEvent *event;
                    447: {
                    448:     StartScroll( gw, event, 'F' );
                    449: };
                    450: 
                    451: static void StartSmoothScroll( gw, event )
                    452:    Widget gw;
                    453:    XEvent *event;
                    454: {
                    455:     StartScroll( gw, event, 'S' );
                    456: };
                    457: 
                    458: 
                    459: static void DoScroll( gw, event, direction )
                    460:    Widget gw;
                    461:    XEvent *event;
                    462:    char direction;
                    463: {
                    464:     ScrollbarWidget w = (ScrollbarWidget) gw;
                    465: 
                    466:     if (w->scrollbar.direction == 0) return;
                    467: 
                    468:     XDefineCursor(XtDisplay(w), XtWindow(w), w->scrollbar.inactiveCursor);
                    469:     XFlush(XtDisplay(w));
                    470: 
                    471:     switch( direction ) {
                    472:         case 'B':
                    473:        case 'b':
                    474:         case 'F':
                    475:        case 'f':    if (w->scrollbar.scrollProc)
                    476:                        (*(w->scrollbar.scrollProc))
                    477:                          ( w, w->scrollbar.closure,
                    478:                            InRange( PICKLENGTH( w,
                    479:                                                 event->xmotion.x,
                    480:                                                 event->xmotion.y),
                    481:                                     0,
                    482:                                     (int)PICKLENGTH( w,
                    483:                                                      w->core.width,
                    484:                                                      w->core.height)));
                    485:                     break;
                    486: 
                    487:         case 'S':
                    488:        case 's':    /* MoveThumb has already called the thumbProc(s) */
                    489:                     break;
                    490:     }
                    491: 
                    492:     w->scrollbar.direction = 0;
                    493: 
                    494: }
                    495: 
                    496: /* The following are only needed until the TM implements args */
                    497: 
                    498: static void DoPageBack( gw, event )
                    499:    Widget gw;
                    500:    XEvent *event;
                    501: {
                    502:     DoScroll( gw, event, 'B' );
                    503: }
                    504: 
                    505: static void DoPageForward( gw, event )
                    506:    Widget gw;
                    507:    XEvent *event;
                    508: {
                    509:     DoScroll( gw, event, 'F' );
                    510: };
                    511: 
                    512: static void DoSmoothScroll( gw, event )
                    513:    Widget gw;
                    514:    XEvent *event;
                    515: {
                    516:     DoScroll( gw, event, 'S' );
                    517: };
                    518: 
                    519: 
                    520: 
                    521: static void MoveThumb( gw, event )
                    522:    Widget gw;
                    523:    XEvent *event;
                    524: {
                    525:     ScrollbarWidget w = (ScrollbarWidget) gw;
                    526: 
                    527:     if (w->scrollbar.direction == 0) return;
                    528: 
                    529:     w->scrollbar.top = FractionLoc(w, event->xmotion.x, event->xmotion.y);
                    530:     PaintThumb(w);
                    531: 
                    532: /*    XFlush(XtDisplay(w)); */
                    533: 
                    534:     if (w->scrollbar.thumbProc)
                    535:        (*(w->scrollbar.thumbProc)) (w, w->scrollbar.closure, w->scrollbar.top);
                    536: 
                    537:     w->scrollbar.direction = 'S';
                    538: }
                    539: 
                    540: 
                    541: 
                    542: /* Public routines. */
                    543: 
                    544: /* Set the scroll bar to the given location. */
                    545: extern void XtScrollBarSetThumb( w, top, shown )
                    546:   ScrollbarWidget w;
                    547:   float top, shown;
                    548: {
                    549:     w->scrollbar.top = top;
                    550:     w->scrollbar.shown = shown;
                    551:     PaintThumb( w );
                    552: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.