Changeset 2125


Ignore:
Timestamp:
Mar 9, 2011 3:28:24 PM (10 years ago)
Author:
mmc
Message:

Finished the first cut at the Diffview widget. Works okay now.
Added the original paper about diff from Hunt/McIlroy?. It helps
to explain the algorithm.

Location:
trunk/gui/src
Files:
1 added
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/gui/src/RpDiffview.c

    r2119 r2125  
    33 *  RpDiffview - like a textbox, but for showing diffs
    44 *
    5  *  Loads two strings, performs a diff, and shows the results.
     5 *  Loads two strings, performs a diff, and shows the results.  Use
     6 *  it as follows:
     7 *
     8 *  Rappture::Diffview .dv \
     9 *      -addedbackground blue -addedforeground white \
     10 *      -deletedbackground red -deletedforeground white -overstrike true \
     11 *      -changedbackground black -changedforeground yellow \
     12 *      -xscrollcommand {.x set} -yscrollcommand {.y set}
     13 * 
     14 *  scrollbar .x -orient horizontal -command {.dv xview}
     15 *  scrollbar .y -orient vertical -command {.dv yview}
     16 * 
     17 *  .dv text 1 [exec cat /tmp/file1]
     18 *  .dv text 2 [exec cat /tmp/file2]
     19 *  .dv configure -diff 2->1 -layout sidebyside
     20 * 
     21 *  puts "DIFF OUTPUT:"
     22 *  puts [.dv diffs -std]
     23 *
    624 * ======================================================================
    725 *  AUTHOR:  Michael McLennan, Purdue University
     
    1432#include "tk.h"
    1533#include <string.h>
     34#include <stdlib.h>
    1635
    1736/*
     
    4665    int numLines;               /* number of lines */
    4766    int maxLines;               /* max size of storage in startPtr/endPtr */
    48     int maxWidth;               /* max length in pixels for all lines */
    4967    char **startPtr;            /* array of start pointers for each line */
    5068    int *lenPtr;                /* array of lengths for each line */
     
    7795    int fromIndex2;             /* starts at this line in buffer 2 */
    7896    int toIndex2;               /* ends at this line in buffer 2 */
     97    int fromWorld;              /* starts at this line in world view */
     98    int toWorld;                /* ends at this line in world view */
    7999} DiffviewDiffOp;
    80100
     
    84104    DiffviewDiffOp *ops;        /* list of diffs */
    85105} DiffviewDiffs;
     106
     107/*
     108 * Data structure used for each line of the final layout:
     109 */
     110typedef struct {
     111    char style;                 /* layout style: 'a'=add, 'd'=del, etc. */
     112    char bnum;                  /* show line from this buffer */
     113    int bline;                  /* line number in buffer bnum */
     114    int diffNum;                /* line is part of this diff */
     115} DiffviewLayoutLine;
     116
     117typedef struct {
     118    int maxLines;               /* maximum number of lines allocated */
     119    int numLines;               /* current number of lines in layout */
     120    DiffviewLayoutLine *lines;  /* info for all lines in world view */
     121} DiffviewLayout;
    86122
    87123/*
     
    109145    DiffviewBuffer buffer[2];   /* text to diff -- buffers #1 and #2 */
    110146    DiffviewDiffs *diffsPtr;    /* diff: longest common subseq btwn buffers */
    111 
    112     enum diffdir diffdir;       /* diff between these buffers */
    113     enum layoutStyle layout;    /* layout style (side-by-side or inline) */
    114     int maxWidth;               /* maximum width of world view in pixels */
    115     int maxHeight;              /* maximum height of world view in pixels */
    116     int lineHeight;             /* height of a line with current font */
    117     int lineAscent;             /* ascent of a line with current font */
    118     int topLine;                /* index of topmost line in view */
    119     int btmLine;                /* index of bottommost line in view */
    120     int fullLines;              /* number of full lines that fit in view */
    121 
    122     int xOffset;                /* offset in pixels to left edge of view */
    123     int xScrollUnit;            /* num pixels for one unit of horizontal
    124                                  * scrolling (one average character) */
    125     int yOffset;                /* offset in pixels to top edge of view */
    126     int yScrollUnit;            /* num pixels for one unit of vertical
    127                                  * scrolling (one average line) */
     147    DiffviewLayout worldview;   /* array mapping each real line in world
     148                                 * coordinates to the corresponding line
     149                                 * in buffer #1/#2, along with its style */
     150
     151    enum diffdir diffdir;       /* diff between these buffers */
     152    enum layoutStyle layout;    /* layout style (side-by-side or inline) */
     153    int maxWidth;               /* maximum width of world view in pixels */
     154    int maxHeight;              /* maximum height of world view in pixels */
     155    int lineHeight;             /* height of a line with current font */
     156    int lineAscent;             /* ascent of a line with current font */
     157    int topLine;                /* index of topmost line in view */
     158    int btmLine;                /* index of bottommost line in view */
     159    int fullLines;              /* number of full lines that fit in view */
     160
     161    int xOffset;                /* offset in pixels to left edge of view */
     162    int xScrollUnit;            /* num pixels for one unit of horizontal
     163                                 * scrolling (one average character) */
     164    int yOffset;                /* offset in pixels to top edge of view */
     165    int yScrollUnit;            /* num pixels for one unit of vertical
     166                                 * scrolling (one average line) */
    128167    char *takeFocus;            /* value of -takefocus option */
    129168    char *yScrollCmd;           /* command prefix for scrolling */
    130169    char *xScrollCmd;           /* command prefix for scrolling */
    131170
    132     int scanMarkX;              /* x-coord for "scan mark" */
    133     int scanMarkY;              /* y-coord for "scan mark" */
    134     int scanMarkXStart;         /* xOffset at start of scan */
    135     int scanMarkYStart;         /* yOffset at start of scan */
     171    int scanMarkX;              /* x-coord for "scan mark" */
     172    int scanMarkY;              /* y-coord for "scan mark" */
     173    int scanMarkXStart;         /* xOffset at start of scan */
     174    int scanMarkYStart;         /* yOffset at start of scan */
    136175
    137176
     
    139178    Tk_Font tklastfont;         /* previous text font (so we detect changes) */
    140179    XColor *fgColorPtr;         /* normal foreground color */
    141     GC textGC;                  /* GC for drawing text */
     180    XColor *addBgColorPtr;      /* background for "added" text in diff */
     181    XColor *addFgColorPtr;      /* foreground for "added" text in diff */
     182    XColor *delBgColorPtr;      /* background for "deleted" text in diff */
     183    XColor *delFgColorPtr;      /* foreground for "deleted" text in diff */
     184    XColor *chgBgColorPtr;      /* background for "changed" text in diff */
     185    XColor *chgFgColorPtr;      /* foreground for "changed" text in diff */
     186    int overStrDel;             /* non-zero => overstrike deleted text */
     187    GC normGC;                  /* GC for drawing normal text */
     188    GC addFgGC;                 /* GC for drawing "added" text in diff */
     189    GC delFgGC;                 /* GC for drawing "deleted" text in diff */
     190    GC chgFgGC;                 /* GC for drawing "changed" text in diff */
    142191    int width;                  /* overall width of widget, in pixels */
    143192    int height;                 /* overall height of widget, in pixels */
     
    172221#define NORMAL_BG                       "#d9d9d9"
    173222
     223#define DEF_DIFFVIEW_ADDBG              "#ccffcc"
     224#define DEF_DIFFVIEW_ADDFG              BLACK
    174225#define DEF_DIFFVIEW_BG_COLOR           NORMAL_BG
    175226#define DEF_DIFFVIEW_BG_MONO            WHITE
    176227#define DEF_DIFFVIEW_BORDERWIDTH        "2"
     228#define DEF_DIFFVIEW_CHGBG              "#ffffcc"
     229#define DEF_DIFFVIEW_CHGFG              BLACK
    177230#define DEF_DIFFVIEW_CURSOR             ""
     231#define DEF_DIFFVIEW_DELBG              "#ffcccc"
     232#define DEF_DIFFVIEW_DELFG              "#666666"
    178233#define DEF_DIFFVIEW_DIFF               "1->2"
    179234#define DEF_DIFFVIEW_FG                 BLACK
    180 #define DEF_DIFFVIEW_FONT               "*-Helvetica-Medium-R-Normal-*-12-120-*"
     235#define DEF_DIFFVIEW_FONT               "Courier -12"
    181236#define DEF_DIFFVIEW_HEIGHT             "2i"
    182237#define DEF_DIFFVIEW_HIGHLIGHT_BG       NORMAL_BG
     
    184239#define DEF_DIFFVIEW_HIGHLIGHT_WIDTH    "1"
    185240#define DEF_DIFFVIEW_LAYOUT             "inline"
     241#define DEF_DIFFVIEW_OVERSTRDEL         "true"
    186242#define DEF_DIFFVIEW_RELIEF             "sunken"
    187243#define DEF_DIFFVIEW_TAKE_FOCUS         (char*)NULL
     
    194250 */
    195251static Tk_OptionSpec optionSpecs[] = {
     252    {TK_OPTION_COLOR, "-addedbackground", "addedBackground", "Background",
     253         DEF_DIFFVIEW_ADDBG, -1, Tk_Offset(Diffview, addBgColorPtr),
     254         TK_OPTION_NULL_OK, 0, 0},
     255    {TK_OPTION_COLOR, "-addedforeground", "addedForeground", "Foreground",
     256         DEF_DIFFVIEW_ADDFG, -1, Tk_Offset(Diffview, addFgColorPtr),
     257         TK_OPTION_NULL_OK, 0, 0},
    196258    {TK_OPTION_BORDER, "-background", "background", "Background",
    197259         DEF_DIFFVIEW_BG_COLOR, -1, Tk_Offset(Diffview, normalBorder),
     
    204266         DEF_DIFFVIEW_BORDERWIDTH, -1, Tk_Offset(Diffview, borderWidth),
    205267         0, 0, 0},
     268    {TK_OPTION_COLOR, "-changedbackground", "changedBackground", "Background",
     269         DEF_DIFFVIEW_CHGBG, -1, Tk_Offset(Diffview, chgBgColorPtr),
     270         TK_OPTION_NULL_OK, 0, 0},
     271    {TK_OPTION_COLOR, "-changedforeground", "changedForeground", "Foreground",
     272         DEF_DIFFVIEW_CHGFG, -1, Tk_Offset(Diffview, chgFgColorPtr),
     273         TK_OPTION_NULL_OK, 0, 0},
    206274    {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor",
    207275         DEF_DIFFVIEW_CURSOR, -1, Tk_Offset(Diffview, cursor),
     276         TK_OPTION_NULL_OK, 0, 0},
     277    {TK_OPTION_COLOR, "-deletedbackground", "deletedBackground", "Background",
     278         DEF_DIFFVIEW_DELBG, -1, Tk_Offset(Diffview, delBgColorPtr),
     279         TK_OPTION_NULL_OK, 0, 0},
     280    {TK_OPTION_COLOR, "-deletedforeground", "deletedForeground", "Foreground",
     281         DEF_DIFFVIEW_DELFG, -1, Tk_Offset(Diffview, delFgColorPtr),
    208282         TK_OPTION_NULL_OK, 0, 0},
    209283    {TK_OPTION_STRING_TABLE, "-diff", "diff", "Diff",
     
    230304        DEF_DIFFVIEW_LAYOUT, -1, Tk_Offset(Diffview, layout),
    231305        0, (ClientData)layoutStyleStrings, 0},
     306    {TK_OPTION_BOOLEAN, "-overstrike", "overstrike", "Overstrike",
     307         DEF_DIFFVIEW_OVERSTRDEL, -1, Tk_Offset(Diffview, overStrDel), 0, 0, 0},
    232308    {TK_OPTION_RELIEF, "-relief", "relief", "Relief",
    233309         DEF_DIFFVIEW_RELIEF, -1, Tk_Offset(Diffview, relief), 0, 0, 0},
     
    273349                            Tcl_Obj *CONST objv[]));
    274350static int              DiffviewBboxSubCmd _ANSI_ARGS_ ((Tcl_Interp *interp,
    275                             Diffview *dvPtr, int index));
     351                            Diffview *dvPtr, int objc, Tcl_Obj *CONST objv[]));
    276352static int              DiffviewDiffsSubCmd _ANSI_ARGS_ ((Tcl_Interp *interp,
    277353                            Diffview *dvPtr, int objc, Tcl_Obj *CONST objv[]));
     
    282358static int              DiffviewYviewSubCmd _ANSI_ARGS_ ((Tcl_Interp *interp,
    283359                            Diffview *dvPtr, int objc, Tcl_Obj *CONST objv[]));
     360static int              DiffviewGetIndex _ANSI_ARGS_ ((Tcl_Interp *interp,
     361                            Diffview *dvPtr, Tcl_Obj *objPtr, int *linePtr,
     362                            DiffviewDiffOp **diffOpPtrPtr));
    284363
    285364static void             DestroyDiffview _ANSI_ARGS_((char *memPtr));
     
    319398static void             DiffviewDiffsFree _ANSI_ARGS_((
    320399                            DiffviewDiffs *diffsPtr));
     400static void             DiffviewLayoutAdd _ANSI_ARGS_((
     401                            DiffviewLayout *layoutPtr,
     402                            DiffviewLayoutLine *linePtr));
     403static void             DiffviewLayoutClear _ANSI_ARGS_((
     404                            DiffviewLayout *layoutPtr));
     405static void             DiffviewLayoutFree _ANSI_ARGS_((
     406                            DiffviewLayout *layoutPtr));
    321407
    322408/*
     
    410496    dvPtr->cursor = None;
    411497    dvPtr->relief = TK_RELIEF_SUNKEN;
    412     dvPtr->textGC = None;
     498    dvPtr->normGC = None;
     499    dvPtr->addFgGC = None;
     500    dvPtr->delFgGC = None;
     501    dvPtr->chgFgGC = None;
    413502    dvPtr->xScrollUnit = 1;
    414503    dvPtr->yScrollUnit = 1;
     
    460549    Diffview *dvPtr = (Diffview*)clientData;
    461550    int result = TCL_OK;
    462     int cmdToken, index;
     551    int cmdToken;
    463552   
    464553    if (objc < 2) {
     
    482571    switch (cmdToken) {
    483572        case COMMAND_BBOX: {
    484             if (objc != 3) {
    485                 Tcl_WrongNumArgs(interp, 2, objv, "index");
    486                 result = TCL_ERROR;
    487                 break;
    488             }
    489             result = Tcl_GetIntFromObj(interp, objv[2], &index);
    490             if (result != TCL_OK) {
    491                 break;
    492             }
    493             result = DiffviewBboxSubCmd(interp, dvPtr, index);
     573            result = DiffviewBboxSubCmd(interp, dvPtr, objc, objv);
    494574            break;
    495575        }
     
    579659
    580660        case COMMAND_SEE: {
    581             int maxLines;
     661            DiffviewDiffOp *diffOpPtr;
     662            int line1, line2, topline;
     663
    582664            if (objc != 3) {
    583665                Tcl_WrongNumArgs(interp, 2, objv, "index");
     
    585667                break;
    586668            }
    587             result = Tcl_GetIntFromObj(interp, objv[2], &index);
     669            result = DiffviewGetIndex(interp, dvPtr, objv[2],
     670                &line1, &diffOpPtr);
    588671            if (result != TCL_OK) {
    589672                break;
    590673            }
    591674
    592             maxLines = dvPtr->maxHeight/dvPtr->lineHeight;
    593             if (index >= maxLines) {
    594                 index = maxLines-1;
    595             }
    596             if (index < 0) {
    597                 index = 0;
    598             }
    599 
    600             if (index < dvPtr->topLine || index > dvPtr->btmLine) {
     675            if (line1 < 0 && diffOpPtr != NULL) {
     676                line1 = diffOpPtr->fromWorld;
     677                line2 = diffOpPtr->toWorld;
     678            } else {
     679                line2 = line1;
     680            }
     681
     682            if (line1 >= dvPtr->worldview.numLines) {
     683                line1 = line2 = dvPtr->worldview.numLines-1;
     684            }
     685            if (line1 < 0) {
     686                line1 = line2 = 0;
     687            }
     688
     689            if (line1 < dvPtr->topLine || line1 > dvPtr->btmLine) {
    601690                /* location off screen? then center it in y-view */
    602                 index -= dvPtr->fullLines/2;
    603                 if (index < 0) {
    604                     index = 0;
     691                if (line2-line1 >= dvPtr->fullLines) {
     692                    topline = line1;
     693                } else {
     694                    topline = (line1+line2)/2 - dvPtr->fullLines/2;
     695                    if (topline < 0) topline = 0;
    605696                }
    606                 ChangeDiffviewView(dvPtr, index);
     697                ChangeDiffviewView(dvPtr, topline);
    607698            }
    608699            result = TCL_OK;
     
    640731 */
    641732static int
    642 DiffviewBboxSubCmd(interp, dvPtr, index)
     733DiffviewBboxSubCmd(interp, dvPtr, objc, objv)
    643734    Tcl_Interp *interp;       /* interp handling this command */
    644735    Diffview *dvPtr;          /* widget data */
    645     int index;                /* desired element for bounding box */
    646 {
    647 #ifdef FOO
    648     int lastVisibleIndex;
    649     /* Determine the index of the last visible item in the listbox */
    650     lastVisibleIndex = dvPtr->topIndex + dvPtr->fullLines
    651         + dvPtr->partialLine;
    652     if (dvPtr->nElements < lastVisibleIndex) {
    653         lastVisibleIndex = dvPtr->nElements;
    654     }
    655 
    656     /* Only allow bbox requests for indices that are visible */
    657     if ((dvPtr->topIndex <= index) && (index < lastVisibleIndex)) {
    658         char buf[TCL_INTEGER_SPACE * 4];
    659         Tcl_Obj *el;
    660         char *stringRep;
    661         int pixelWidth, stringLen, x, y, result;
    662         Tk_FontMetrics fm;
    663 
    664         /* Compute the pixel width of the requested element */
    665         result = Tcl_ListObjIndex(interp, dvPtr->listObj, index, &el);
    666         if (result != TCL_OK) {
    667             return result;
    668         }
    669 
    670         stringRep = Tcl_GetStringFromObj(el, &stringLen);
    671         Tk_GetFontMetrics(dvPtr->tkfont, &fm);
    672         pixelWidth = Tk_TextWidth(dvPtr->tkfont, stringRep, stringLen);
    673 
    674         x = dvPtr->inset - dvPtr->xOffset;
    675         y = ((index - dvPtr->topIndex)*dvPtr->lineHeight) + dvPtr->inset;
    676         sprintf(buf, "%d %d %d %d", x, y, pixelWidth, fm.linespace);
     736    int objc;                 /* number of command arguments */
     737    Tcl_Obj *CONST objv[];    /* command arguments */
     738{
     739    char *opt, buf[256];
     740    DiffviewDiffOp *diffOpPtr;
     741    int line, bnum, bline, x1, y0, y1, wd;
     742    char *textPtr; int textLen;
     743
     744    if (objc != 3) {
     745        Tcl_WrongNumArgs(interp, 2, objv, "index");
     746        return TCL_ERROR;
     747    }
     748
     749    /* make sure that our layout info is up to date for queries below */
     750    DiffviewUpdateLayout(dvPtr);
     751
     752    opt = Tcl_GetString(objv[2]);
     753    if (*opt == 'a' && strcmp(opt,"all") == 0) {
     754        sprintf(buf, "0 0 %d %d", dvPtr->maxWidth, dvPtr->maxHeight);
    677755        Tcl_SetResult(interp, buf, TCL_VOLATILE);
    678     }
    679 #endif
     756        return TCL_OK;
     757    }
     758
     759    if (DiffviewGetIndex(interp, dvPtr, objv[2], &line, &diffOpPtr) != TCL_OK) {
     760        Tcl_AppendResult(interp, " or all", (char*)NULL);
     761        return TCL_ERROR;
     762    }
     763
     764    /* return the bounding box for a line in world coords */
     765    if (line >= 0) {
     766        y0 = line*dvPtr->lineHeight;
     767        y1 = (line+1)*dvPtr->lineHeight;
     768        x1 = 0;
     769        if (line < dvPtr->worldview.numLines) {
     770            bnum = dvPtr->worldview.lines[line].bnum;
     771            bline = dvPtr->worldview.lines[line].bline;
     772            if (bline >= 0) {
     773                textPtr = dvPtr->buffer[bnum].lineLimits->startPtr[bline];
     774                textLen = dvPtr->buffer[bnum].lineLimits->lenPtr[bline];
     775                x1 = Tk_TextWidth(dvPtr->tkfont, textPtr, textLen);
     776            }
     777        }
     778        sprintf(buf, "0 %d %d %d", y0, x1, y1);
     779        Tcl_SetResult(interp, buf, TCL_VOLATILE);
     780        return TCL_OK;
     781    }
     782
     783    /* return the bounding box for a diff in world coords */
     784    if (diffOpPtr) {
     785        y0 = diffOpPtr->fromWorld*dvPtr->lineHeight;
     786        y1 = (diffOpPtr->toWorld+1)*dvPtr->lineHeight;
     787        x1 = 0;
     788
     789        for (line=diffOpPtr->fromWorld; line <= diffOpPtr->toWorld; line++) {
     790            if (line < dvPtr->worldview.numLines) {
     791                bnum = dvPtr->worldview.lines[line].bnum;
     792                bline = dvPtr->worldview.lines[line].bline;
     793                if (bline >= 0) {
     794                    textPtr = dvPtr->buffer[bnum].lineLimits->startPtr[bline];
     795                    textLen = dvPtr->buffer[bnum].lineLimits->lenPtr[bline];
     796                    wd = Tk_TextWidth(dvPtr->tkfont, textPtr, textLen);
     797                    if (wd > x1) {
     798                        x1 = wd;
     799                    }
     800                }
     801            }
     802        }
     803        sprintf(buf, "0 %d %d %d", y0, x1, y1);
     804        Tcl_SetResult(interp, buf, TCL_VOLATILE);
     805    }
    680806    return TCL_OK;
    681807}
     
    11251251/*
    11261252 * ----------------------------------------------------------------------
     1253 * DiffviewGetIndex()
     1254 *
     1255 * Used by many of the widget operations to parse an index into the
     1256 * widget view.  Handles the following syntax:
     1257 *   #NN ...... particular diff number, starting from 1
     1258 *   NN ....... particular line number in world view, starting from 0
     1259 *
     1260 * Returns TCL_OK if successful, and TCL_ERROR (along with an error
     1261 * message) if anything goes wrong.
     1262 * ----------------------------------------------------------------------
     1263 */
     1264static int
     1265DiffviewGetIndex(interp, dvPtr, objPtr, linePtr, diffOpPtrPtr)
     1266    Tcl_Interp *interp;             /* interp handling this command */
     1267    Diffview *dvPtr;                /* widget data */
     1268    Tcl_Obj *objPtr;                /* argument being parsed */
     1269    int *linePtr;                   /* returns: line number or -1 */
     1270    DiffviewDiffOp **diffOpPtrPtr;  /* returns: specific diff or NULL */
     1271{
     1272    int result = TCL_OK;
     1273    char *opt = Tcl_GetString(objPtr);
     1274    char *tail;
     1275    int dnum;
     1276
     1277    /* clear the return values */
     1278    if (linePtr) *linePtr = -1;
     1279    if (diffOpPtrPtr) *diffOpPtrPtr = NULL;
     1280
     1281    if (*opt == '#') {
     1282        dnum = strtol(opt+1, &tail, 10);
     1283        if (*tail != '\0' || dnum <= 0) {
     1284           result = TCL_ERROR;
     1285        }
     1286        else if (dvPtr->diffsPtr == NULL
     1287                   || dnum > dvPtr->diffsPtr->numDiffs) {
     1288           Tcl_AppendResult(interp, "diff \"", opt, "\" doesn't exist",
     1289               (char*)NULL);
     1290           return TCL_ERROR;
     1291        }
     1292        *diffOpPtrPtr = &dvPtr->diffsPtr->ops[dnum-1];
     1293    } else {
     1294        result = Tcl_GetIntFromObj(interp, objPtr, linePtr);
     1295    }
     1296
     1297    if (result != TCL_OK) {
     1298        Tcl_ResetResult(interp);
     1299        Tcl_AppendResult(interp, "bad index \"", opt, "\": should be "
     1300            "\"#n\" for diff, or an integer value for line number",
     1301            (char*)NULL);
     1302    }
     1303    return result;
     1304}
     1305
     1306/*
     1307 * ----------------------------------------------------------------------
    11271308 * DestroyDiffview()
    11281309 *
     
    11541335        DiffviewDiffsFree(dvPtr->diffsPtr);
    11551336    }
     1337    DiffviewLayoutFree(&dvPtr->worldview);
    11561338
    11571339    /*
    11581340     * Free up GCs and configuration options.
    11591341     */
    1160     if (dvPtr->textGC != None) {
    1161         Tk_FreeGC(dvPtr->display, dvPtr->textGC);
    1162     }
     1342    if (dvPtr->normGC != None) {
     1343        Tk_FreeGC(dvPtr->display, dvPtr->normGC);
     1344    }
     1345    if (dvPtr->addFgGC != None) {
     1346        Tk_FreeGC(dvPtr->display, dvPtr->addFgGC);
     1347    }
     1348    if (dvPtr->delFgGC != None) {
     1349        Tk_FreeGC(dvPtr->display, dvPtr->delFgGC);
     1350    }
     1351    if (dvPtr->chgFgGC != None) {
     1352        Tk_FreeGC(dvPtr->display, dvPtr->chgFgGC);
     1353    }
     1354
    11631355    Tk_FreeConfigOptions((char*)dvPtr, dvPtr->optionTable, dvPtr->tkwin);
    11641356    Tcl_Release((ClientData) dvPtr->tkwin);
     
    12401432    unsigned long mask;
    12411433
     1434    /* GC for normal widget text */
    12421435    gcValues.foreground = dvPtr->fgColorPtr->pixel;
    12431436    gcValues.graphics_exposures = False;
     
    12461439
    12471440    gc = Tk_GetGC(dvPtr->tkwin, mask, &gcValues);
    1248     if (dvPtr->textGC != None) {
    1249         Tk_FreeGC(dvPtr->display, dvPtr->textGC);
    1250     }
    1251     dvPtr->textGC = gc;
     1441    if (dvPtr->normGC != None) {
     1442        Tk_FreeGC(dvPtr->display, dvPtr->normGC);
     1443    }
     1444    dvPtr->normGC = gc;
     1445
     1446    /* GC for added diff text */
     1447    gcValues.foreground = dvPtr->addFgColorPtr->pixel;
     1448    gcValues.graphics_exposures = False;
     1449    gcValues.font = Tk_FontId(dvPtr->tkfont);
     1450    mask = GCForeground | GCFont | GCGraphicsExposures;
     1451
     1452    gc = Tk_GetGC(dvPtr->tkwin, mask, &gcValues);
     1453    if (dvPtr->addFgGC != None) {
     1454        Tk_FreeGC(dvPtr->display, dvPtr->addFgGC);
     1455    }
     1456    dvPtr->addFgGC = gc;
     1457
     1458    /* GC for deleted diff text */
     1459    gcValues.foreground = dvPtr->delFgColorPtr->pixel;
     1460    gcValues.graphics_exposures = False;
     1461    gcValues.font = Tk_FontId(dvPtr->tkfont);
     1462    mask = GCForeground | GCFont | GCGraphicsExposures;
     1463
     1464    gc = Tk_GetGC(dvPtr->tkwin, mask, &gcValues);
     1465    if (dvPtr->delFgGC != None) {
     1466        Tk_FreeGC(dvPtr->display, dvPtr->delFgGC);
     1467    }
     1468    dvPtr->delFgGC = gc;
     1469
     1470    /* GC for changed diff text */
     1471    gcValues.foreground = dvPtr->chgFgColorPtr->pixel;
     1472    gcValues.graphics_exposures = False;
     1473    gcValues.font = Tk_FontId(dvPtr->tkfont);
     1474    mask = GCForeground | GCFont | GCGraphicsExposures;
     1475
     1476    gc = Tk_GetGC(dvPtr->tkwin, mask, &gcValues);
     1477    if (dvPtr->chgFgGC != None) {
     1478        Tk_FreeGC(dvPtr->display, dvPtr->chgFgGC);
     1479    }
     1480    dvPtr->chgFgGC = gc;
     1481
     1482    /* this call comes when the font changes -- fix the layout */
     1483    dvPtr->flags |= FONT_CHANGED;
    12521484
    12531485    /* get ready to redraw */
     
    12911523    Tk_Window tkwin = dvPtr->tkwin;
    12921524
    1293     int i, bnum, x, y;
     1525    int i, bnum, bline, x, xw, y, ymid, width;
    12941526    char *textPtr; int textLen;
    12951527    Pixmap pixmap;
     1528    GC bg, fg;
    12961529
    12971530    /* handling redraw now -- no longer pending */
     
    13501583          - dvPtr->yOffset;
    13511584    x = dvPtr->inset - dvPtr->xOffset;
    1352 
    1353     if (dvPtr->diffdir == DIFF_1TO2) {
    1354         bnum = 1;  /* 1->2 means: show lines from buffer #2 */
    1355     } else {
    1356         bnum = 0;  /* 2->1 means: show lines from buffer #1 */
    1357     }
    1358 
    1359     if (dvPtr->buffer[bnum].lineLimits) {
    1360         for (i=dvPtr->topLine; i <= dvPtr->btmLine; i++) {
    1361             if (i >= dvPtr->buffer[bnum].lineLimits->numLines) {
     1585    width = Tk_Width(tkwin);
     1586
     1587    for (i=dvPtr->topLine;
     1588         i <= dvPtr->btmLine && i < dvPtr->worldview.numLines;
     1589         i++) {
     1590
     1591        /* draw any diff rectangle for this line */
     1592        fg = dvPtr->normGC;
     1593        bg = None;
     1594
     1595        switch (dvPtr->worldview.lines[i].style) {
     1596            case 'a': {
     1597                fg = dvPtr->addFgGC;
     1598                bg = Tk_GCForColor(dvPtr->addBgColorPtr, pixmap);
    13621599                break;
    13631600            }
    1364 
    1365             /* draw any diff rectangle for this line */
    1366             /*
    1367             Tk_Fill3DRectangle(tkwin, pixmap, selectedBg, x, y,
    1368                 width, dvPtr->lineHeight, 0, TK_RELIEF_FLAT);
    1369             */
    1370 
    1371             textPtr = dvPtr->buffer[bnum].lineLimits->startPtr[i];
    1372             textLen = dvPtr->buffer[bnum].lineLimits->lenPtr[i];
     1601            case 'd': {
     1602                fg = dvPtr->delFgGC;
     1603                bg = Tk_GCForColor(dvPtr->delBgColorPtr, pixmap);
     1604                break;
     1605            }
     1606            case 'c': {
     1607                fg = dvPtr->chgFgGC;
     1608                bg = Tk_GCForColor(dvPtr->chgBgColorPtr, pixmap);
     1609                break;
     1610            }
     1611        }
     1612
     1613        if (bg != None) {
     1614            XFillRectangle(Tk_Display(tkwin), pixmap, bg,
     1615                x, y - dvPtr->lineAscent,
     1616                (unsigned int)width, (unsigned int)dvPtr->lineHeight);
     1617        }
     1618
     1619        bnum = dvPtr->worldview.lines[i].bnum;
     1620        bline = dvPtr->worldview.lines[i].bline;
     1621
     1622        /* a negative number means "leave the line blank" */
     1623        if (bline >= 0) {
     1624            textPtr = dvPtr->buffer[bnum].lineLimits->startPtr[bline];
     1625            textLen = dvPtr->buffer[bnum].lineLimits->lenPtr[bline];
    13731626
    13741627            /* Draw the actual text of this item */
    1375             Tk_DrawChars(dvPtr->display, pixmap, dvPtr->textGC, dvPtr->tkfont,
    1376                     textPtr, textLen, x, y);
    1377 
    1378             y += dvPtr->lineHeight;
    1379         }
     1628            Tk_DrawChars(dvPtr->display, pixmap, fg, dvPtr->tkfont,
     1629                textPtr, textLen, x, y);
     1630
     1631            /* Draw an overstrike on deleted text */
     1632            if (dvPtr->worldview.lines[i].style == 'd' && dvPtr->overStrDel) {
     1633                xw = Tk_TextWidth(dvPtr->tkfont, textPtr, textLen) + 5;
     1634                ymid = y - dvPtr->lineAscent/2;
     1635                XDrawLine(Tk_Display(tkwin), pixmap, fg, 0, ymid, xw, ymid);
     1636            }
     1637        }
     1638
     1639        y += dvPtr->lineHeight;
    13801640    }
    13811641
     
    14011661#ifndef TK_NO_DOUBLE_BUFFERING
    14021662    XCopyArea(dvPtr->display, pixmap, Tk_WindowId(tkwin),
    1403             dvPtr->textGC, 0, 0, (unsigned) Tk_Width(tkwin),
     1663            dvPtr->normGC, 0, 0, (unsigned) Tk_Width(tkwin),
    14041664            (unsigned) Tk_Height(tkwin), 0, 0);
    14051665    Tk_FreePixmap(dvPtr->display, pixmap);
     
    16111871    int changes = 0;   /* no layout changes yet */
    16121872    DiffviewBuffer* bufferPtr;
    1613     int i, bnum, pixelWidth, maxWidth, numLines, ySize;
     1873    DiffviewDiffOp *currOp;
     1874    DiffviewLayoutLine line;
     1875    int i, bnum, bline, pixelWidth, maxWidth, ySize;
     1876    int numLines1, numLines2, lnum1, lnum2, dnum, lastdnum, pastDiff;
    16141877    char *textPtr, *textPtr2; int textLen;
    16151878    Tk_FontMetrics fm;
     
    16391902            textPtr = Tcl_GetStringFromObj(dvPtr->buffer[0].textObj, &textLen);
    16401903            textPtr2 = Tcl_GetStringFromObj(dvPtr->buffer[1].textObj, &textLen);
     1904
    16411905            dvPtr->diffsPtr = DiffviewDiffsCreate(
    16421906                textPtr, dvPtr->buffer[0].lineLimits,
     
    16441908        }
    16451909
    1646         /* compute overall size of each buffer */
    1647         for (bnum=0; bnum < 2; bnum++) {
    1648             bufferPtr = &dvPtr->buffer[bnum];
    1649 
    1650             if (bufferPtr->lineLimits) {
    1651                 maxWidth = 0;
    1652 
    1653                 for (i=0; i < bufferPtr->lineLimits->numLines; i++) {
    1654                     textPtr = bufferPtr->lineLimits->startPtr[i];
    1655                     textLen = bufferPtr->lineLimits->lenPtr[i];
     1910        /*
     1911         * Compute the layout of all lines according to the current
     1912         * view mode.  Each line in the world view is stored in the
     1913         * array dvPtr->worldview.lines.  Each line has a style (color
     1914         * for normal, add, delete, etc.) and an indication of which
     1915         * buffer it comes from.
     1916         */
     1917        DiffviewLayoutClear(&dvPtr->worldview);
     1918
     1919        /*
     1920         * March through the lines and figure out the source and style
     1921         * of each line based on the diffs:
     1922         *   'n' = normal (common) line
     1923         *   'a' = draw with the "added" style
     1924         *   'd' = draw with the "deleted" style
     1925         *   'c' = draw with the "changed" style
     1926         */
     1927        numLines1 = (dvPtr->buffer[0].lineLimits)
     1928                       ? dvPtr->buffer[0].lineLimits->numLines : 0;
     1929        numLines2 = (dvPtr->buffer[1].lineLimits)
     1930                       ? dvPtr->buffer[1].lineLimits->numLines : 0;
     1931
     1932        dnum = 0;  /* current difference in diffsPtr */
     1933        lnum1 = lnum2 = 0;
     1934
     1935        while (lnum1 < numLines1 || lnum2 < numLines2) {
     1936            /* assume it's a normal-looking line (buffers are the same) */
     1937            line.style = 'n';
     1938            line.bnum = 0;
     1939            line.bline = lnum1;
     1940            line.diffNum = -1;
     1941
     1942            /* is there a diff that contains this line? */
     1943            if (dvPtr->diffsPtr && dnum < dvPtr->diffsPtr->numDiffs) {
     1944              currOp = &dvPtr->diffsPtr->ops[dnum];
     1945
     1946              if ( (lnum1 >= currOp->fromIndex1
     1947                     && lnum1 <= currOp->toIndex1)
     1948                || (lnum2 >= currOp->fromIndex2
     1949                     && lnum2 <= currOp->toIndex2) ) {
     1950
     1951                line.diffNum = dnum;  /* line is part of this diff */
     1952
     1953                switch (currOp->op) {
     1954                    case 'c': {
     1955                        if (dvPtr->layout == LAYOUT_INLINE) {
     1956                            if (dvPtr->diffdir == DIFF_1TO2) {
     1957                                /* show the buffer #1 lines first, then #2 */
     1958                                if (lnum1 <= currOp->toIndex1) {
     1959                                    line.style = 'd';
     1960                                    line.bnum = 0;
     1961                                    line.bline = lnum1++;
     1962                                } else {
     1963                                    line.style = 'a';
     1964                                    line.bnum = 1;
     1965                                    line.bline = lnum2++;
     1966                                }
     1967                            } else {
     1968                                /* reverse -- show #2 lines first, then #1 */
     1969                                if (lnum2 <= currOp->toIndex2) {
     1970                                    line.style = 'd';
     1971                                    line.bnum = 1;
     1972                                    line.bline = lnum2++;
     1973                                } else {
     1974                                    line.style = 'a';
     1975                                    line.bnum = 0;
     1976                                    line.bline = lnum1++;
     1977                                }
     1978                            }
     1979                        } else {
     1980                            if (dvPtr->diffdir == DIFF_1TO2) {
     1981                                /* show final lines in buf #2 as "changed" */
     1982                                line.style = 'c';
     1983                                line.bnum = 1;
     1984                                line.bline = lnum2++;
     1985                                lnum1 = currOp->toIndex1+1;
     1986                            } else {
     1987                                /* show final lines in buf #1 as "changed" */
     1988                                line.style = 'c';
     1989                                line.bnum = 0;
     1990                                line.bline = lnum1++;
     1991                                lnum2 = currOp->toIndex2+1;
     1992                            }
     1993                        }
     1994                        break;
     1995                    }
     1996                    case 'a': {
     1997                        if (dvPtr->diffdir == DIFF_1TO2) {
     1998                            /* show lines in buffer #2 as 'added' */
     1999                            line.style = 'a';
     2000                            line.bnum = 1;
     2001                            line.bline = lnum2++;
     2002                            lnum1 = currOp->toIndex1;
     2003                        } else {
     2004                            /* reverse diff -- like we're deleting lines */
     2005                            if (dvPtr->layout == LAYOUT_INLINE) {
     2006                                line.style = 'd';
     2007                                line.bnum = 1;
     2008                                line.bline = lnum2++;
     2009                                lnum1 = currOp->toIndex1;
     2010                            } else {
     2011                                /* show lines from buffer #2 as empty */
     2012                                line.style = 'd';
     2013                                line.bnum = 0;
     2014                                line.bline = -1;
     2015                                lnum2++;
     2016                                lnum1 = currOp->toIndex1;
     2017                            }
     2018                        }
     2019                        break;
     2020                    }
     2021                    case 'd': {
     2022                        if (dvPtr->diffdir == DIFF_1TO2) {
     2023                            if (dvPtr->layout == LAYOUT_INLINE) {
     2024                                line.style = 'd';
     2025                                line.bnum = 0;
     2026                                line.bline = lnum1++;
     2027                                lnum2 = currOp->toIndex2;
     2028                            } else {
     2029                                /* show lines from buffer #2 as empty */
     2030                                line.style = 'd';
     2031                                line.bnum = 0;
     2032                                line.bline = -1;
     2033                                lnum1++;
     2034                                lnum2 = currOp->toIndex2;
     2035                            }
     2036                        } else {
     2037                            /* reverse diff -- like we're adding lines */
     2038                            line.style = 'a';
     2039                            line.bnum = 0;
     2040                            line.bline = lnum1++;
     2041                            lnum2 = currOp->toIndex2;
     2042                        }
     2043                        break;
     2044                    }
     2045                    default: {
     2046                        Tcl_Panic("bad diff type '%c' found in layout",
     2047                            currOp->op);
     2048                        break;
     2049                    }
     2050                }
     2051              } else {
     2052                /* normal line -- keep moving forward */
     2053                lnum1++; lnum2++;
     2054              }
     2055
     2056              /* have we reached the end of the diff? then move on */
     2057              pastDiff = 0;
     2058              switch (currOp->op) {
     2059                  case 'c':
     2060                      pastDiff = (lnum1 > currOp->toIndex1
     2061                               && lnum2 > currOp->toIndex2);
     2062                      break;
     2063                  case 'a':
     2064                      pastDiff = (lnum2 > currOp->toIndex2);
     2065                      break;
     2066                  case 'd':
     2067                      pastDiff = (lnum1 > currOp->toIndex1);
     2068                      break;
     2069              }
     2070              if (pastDiff) {
     2071                  dnum++;
     2072              }
     2073            } else {
     2074                /* no more diffs -- keep moving forward */
     2075                lnum1++; lnum2++;
     2076            }
     2077
     2078            /* add this new line to the layout */
     2079            DiffviewLayoutAdd(&dvPtr->worldview, &line);
     2080        }
     2081
     2082        /*
     2083         * Figure out where the diffs are located, and put that info
     2084         * back into the diffs.  This makes it easy later to refer to
     2085         * diff "#3" and understand what lines we're talking about.
     2086         */
     2087        lastdnum = -1;
     2088        for (i=0; i < dvPtr->worldview.numLines; i++) {
     2089            dnum = dvPtr->worldview.lines[i].diffNum;
     2090            if (dnum != lastdnum) {
     2091                if (lastdnum < 0) {
     2092                    /* leading edge -- catch the "from" line */
     2093                    lnum1 = i;
     2094                } else {
     2095                    /* trailing edge -- save diff info */
     2096                    dvPtr->diffsPtr->ops[lastdnum].fromWorld = lnum1;
     2097                    dvPtr->diffsPtr->ops[lastdnum].toWorld = i-1;
     2098                }
     2099            }
     2100            lastdnum = dnum;
     2101        }
     2102        if (lastdnum >= 0) {
     2103            dvPtr->diffsPtr->ops[lastdnum].fromWorld = lnum1;
     2104            dvPtr->diffsPtr->ops[lastdnum].toWorld = i-1;
     2105        }
     2106
     2107        /* compute overall text width for all lines */
     2108        maxWidth = 0;
     2109        for (i=0; i < dvPtr->worldview.numLines; i++) {
     2110            bline = dvPtr->worldview.lines[i].bline;
     2111            if (bline >= 0) {
     2112                bnum = dvPtr->worldview.lines[i].bnum;
     2113                bufferPtr = &dvPtr->buffer[bnum];
     2114
     2115                if (bufferPtr->lineLimits) {
     2116                    textPtr = bufferPtr->lineLimits->startPtr[bline];
     2117                    textLen = bufferPtr->lineLimits->lenPtr[bline];
    16562118                    pixelWidth = Tk_TextWidth(dvPtr->tkfont, textPtr, textLen);
    16572119
     
    16602122                    }
    16612123                }
    1662                 bufferPtr->lineLimits->maxWidth = maxWidth;
    1663             }
    1664         }
    1665 
    1666         /* compute overall size of the widget depending on the layout */
    1667         if (dvPtr->diffdir == DIFF_1TO2) {
    1668             bnum = 0;
    1669         } else {
    1670             bnum = 1;
    1671         }
    1672 
    1673         maxWidth = 0;
    1674         numLines = 0;
    1675         if (dvPtr->buffer[bnum].lineLimits) {
    1676             maxWidth = dvPtr->buffer[bnum].lineLimits->maxWidth;
    1677             if (dvPtr->layout == LAYOUT_INLINE) {
    1678                 numLines = dvPtr->buffer[bnum].lineLimits->numLines;
    1679             } else {
    1680                 /* WARNING - should compute the height by looking at diffs */
    1681                 for (i=0; i < 2; i++) {
    1682                     if (dvPtr->buffer[i].lineLimits
    1683                           && dvPtr->buffer[i].lineLimits->numLines > numLines) {
    1684                         numLines = dvPtr->buffer[i].lineLimits->numLines;
    1685                     }
    1686                 }
    16872124            }
    16882125        }
    16892126        dvPtr->maxWidth = maxWidth;
    16902127
     2128        /* compute overall height of the widget */
    16912129        Tk_GetFontMetrics(dvPtr->tkfont, &fm);
    16922130        dvPtr->lineHeight = fm.linespace;
    16932131        dvPtr->lineAscent = fm.ascent;
    1694         dvPtr->maxHeight = numLines * fm.linespace;
     2132        dvPtr->maxHeight = dvPtr->worldview.numLines * fm.linespace;
    16952133
    16962134        /* figure out the limits of the current view */
     
    18402278    lineLimitsPtr = (DiffviewLines*)ckalloc(sizeof(DiffviewLines));
    18412279    lineLimitsPtr->maxLines = 0;
    1842     lineLimitsPtr->maxWidth = 0;
    18432280    lineLimitsPtr->startPtr = NULL;
    18442281    lineLimitsPtr->lenPtr = NULL;
     
    22212658 * DiffviewDiffsFree()
    22222659 *
    2223  * Appends a "diff" operation onto a list of diffs.  The list is extended
    2224  * automatically, if need be, to store the new element.
     2660 * Frees up the storage previously created by calling
     2661 * DiffviewDiffsAppend().
    22252662 * ----------------------------------------------------------------------
    22262663 */
     
    22342671    ckfree((char*)diffsPtr);
    22352672}
     2673
     2674/*
     2675 * ----------------------------------------------------------------------
     2676 * DiffviewLayoutAdd()
     2677 *
     2678 * Clears out the layout storage in preparation for building another
     2679 * layout.  If the previous number of lines was much less than the
     2680 * maximum allocated, then this routine reallocates a smaller storage
     2681 * space.  This provides a way to give back a large chunk of memory
     2682 * if the contents of the widget is nulled out, for example.
     2683 * ----------------------------------------------------------------------
     2684 */
     2685static void
     2686DiffviewLayoutAdd(layoutPtr, linePtr)
     2687    DiffviewLayout *layoutPtr;    /* line layout being updated */
     2688    DiffviewLayoutLine *linePtr;  /* line being added to layout */
     2689{
     2690    DiffviewLayoutLine *newLineArray;
     2691
     2692    /* expand the array as needed */
     2693    if (layoutPtr->numLines >= layoutPtr->maxLines) {
     2694        if (layoutPtr->maxLines == 0) {
     2695            layoutPtr->maxLines = 100;
     2696        } else {
     2697            layoutPtr->maxLines *= 2;
     2698        }
     2699        newLineArray = (DiffviewLayoutLine*)ckalloc(
     2700            (unsigned)(layoutPtr->maxLines*sizeof(DiffviewLayoutLine)));
     2701
     2702        if (layoutPtr->lines) {
     2703            memcpy((VOID*)newLineArray, (VOID*)layoutPtr->lines,
     2704                layoutPtr->numLines*sizeof(DiffviewLayoutLine));
     2705            ckfree((char*)layoutPtr->lines);
     2706        }
     2707        layoutPtr->lines = newLineArray;
     2708    }
     2709
     2710    /* copy the latest line in and bump the count */
     2711    memcpy((VOID*)&layoutPtr->lines[layoutPtr->numLines],
     2712        (VOID*)linePtr, sizeof(DiffviewLayoutLine));
     2713
     2714    layoutPtr->numLines++;
     2715}
     2716
     2717/*
     2718 * ----------------------------------------------------------------------
     2719 * DiffviewLayoutClear()
     2720 *
     2721 * Clears out the layout storage in preparation for building another
     2722 * layout.  If the previous number of lines was much less than the
     2723 * maximum allocated, then this routine reallocates a smaller storage
     2724 * space.  This provides a way to give back a large chunk of memory
     2725 * if the contents of the widget is nulled out, for example.
     2726 * ----------------------------------------------------------------------
     2727 */
     2728static void
     2729DiffviewLayoutClear(layoutPtr)
     2730    DiffviewLayout *layoutPtr;  /* line layout being freed */
     2731{
     2732    if (layoutPtr->numLines > 0
     2733          && layoutPtr->numLines < layoutPtr->maxLines/3
     2734          && layoutPtr->maxLines > 100) {
     2735        ckfree((char*)layoutPtr->lines);
     2736        layoutPtr->lines = (DiffviewLayoutLine*)ckalloc(
     2737            (unsigned)(layoutPtr->numLines * sizeof(DiffviewLayoutLine)));
     2738    }
     2739    layoutPtr->numLines = 0;
     2740}
     2741
     2742/*
     2743 * ----------------------------------------------------------------------
     2744 * DiffviewLayoutFree()
     2745 *
     2746 * Frees the storage associated with the layout of all lines within
     2747 * the widget.
     2748 * ----------------------------------------------------------------------
     2749 */
     2750static void
     2751DiffviewLayoutFree(layoutPtr)
     2752    DiffviewLayout *layoutPtr;  /* line layout being freed */
     2753{
     2754    if (layoutPtr->lines) {
     2755        ckfree((char*)layoutPtr->lines);
     2756        layoutPtr->lines = NULL;
     2757    }
     2758}
Note: See TracChangeset for help on using the changeset viewer.