Fossil SCM
Draw inbound merge arrows on the graph even if the merge parent is off-screen.
Commit
e685fc0b858596f76d3389ba4bc56d0d3f57719f
Parent
1092a1df55920be…
2 files changed
+26
-28
+5
-1
+26
-28
| --- src/graph.c | ||
| +++ src/graph.c | ||
| @@ -21,15 +21,17 @@ | ||
| 21 | 21 | #include "graph.h" |
| 22 | 22 | #include <assert.h> |
| 23 | 23 | |
| 24 | 24 | #if INTERFACE |
| 25 | 25 | |
| 26 | -#define GR_MAX_PARENT 10 /* Most parents for any one node */ | |
| 26 | +#define GR_MAX_PARENT 10 /* Max number of parents for any one node */ | |
| 27 | 27 | #define GR_MAX_RAIL 32 /* Max number of "rails" to display */ |
| 28 | 28 | |
| 29 | 29 | /* The graph appears vertically beside a timeline. Each row in the |
| 30 | -** timeline corresponds to a row in the graph. | |
| 30 | +** timeline corresponds to a row in the graph. GraphRow.idx is 0 for | |
| 31 | +** the top-most row and increases moving down. Hence (in the absence of | |
| 32 | +** time skew) parents have a larger index than their children. | |
| 31 | 33 | */ |
| 32 | 34 | struct GraphRow { |
| 33 | 35 | int rid; /* The rid for the check-in */ |
| 34 | 36 | int nParent; /* Number of parents */ |
| 35 | 37 | int aParent[GR_MAX_PARENT]; /* Array of parents. 0 element is primary .*/ |
| @@ -47,10 +49,11 @@ | ||
| 47 | 49 | int aiRaiser[GR_MAX_RAIL]; /* Raisers from this node to a higher row. */ |
| 48 | 50 | int bDescender; /* Raiser from bottom of graph to here. */ |
| 49 | 51 | u32 mergeIn; /* Merge in from other rails */ |
| 50 | 52 | int mergeOut; /* Merge out to this rail */ |
| 51 | 53 | int mergeUpto; /* Draw the merge rail up to this level */ |
| 54 | + u32 mergeDown; /* Draw merge lines up from bottom of graph */ | |
| 52 | 55 | |
| 53 | 56 | u32 railInUse; /* Mask of occupied rails */ |
| 54 | 57 | }; |
| 55 | 58 | |
| 56 | 59 | /* Context while building a graph |
| @@ -277,27 +280,10 @@ | ||
| 277 | 280 | } |
| 278 | 281 | hashInsert(p, pRow, 1); |
| 279 | 282 | } |
| 280 | 283 | p->mxRail = -1; |
| 281 | 284 | |
| 282 | - /* Purge merge-parents that are out-of-graph. | |
| 283 | - ** | |
| 284 | - ** Each node has one primary parent and zero or more "merge" parents. | |
| 285 | - ** A merge parent is a prior checkin from which changes were merged into | |
| 286 | - ** the current check-in. If a merge parent is not in the visible section | |
| 287 | - ** of this graph, then no arrows will be drawn for it, so remove it from | |
| 288 | - ** the aParent[] array. | |
| 289 | - */ | |
| 290 | - for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ | |
| 291 | - for(i=1; i<pRow->nParent; i++){ | |
| 292 | - if( hashFind(p, pRow->aParent[i])==0 ){ | |
| 293 | - pRow->aParent[i] = pRow->aParent[--pRow->nParent]; | |
| 294 | - i--; | |
| 295 | - } | |
| 296 | - } | |
| 297 | - } | |
| 298 | - | |
| 299 | 285 | /* Find the pChild pointer for each node. |
| 300 | 286 | ** |
| 301 | 287 | ** The pChild points to the node directly above on the same rail. |
| 302 | 288 | ** The pChild must be in the same branch. Leaf nodes have a NULL |
| 303 | 289 | ** pChild. |
| @@ -398,22 +384,33 @@ | ||
| 398 | 384 | */ |
| 399 | 385 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 400 | 386 | for(i=1; i<pRow->nParent; i++){ |
| 401 | 387 | int parentRid = pRow->aParent[i]; |
| 402 | 388 | pDesc = hashFind(p, parentRid); |
| 403 | - if( pDesc==0 ) continue; | |
| 404 | - if( pDesc->mergeOut<0 ){ | |
| 405 | - int iTarget = (pRow->iRail + pDesc->iRail)/2; | |
| 406 | - pDesc->mergeOut = findFreeRail(p, pRow->idx, pDesc->idx-1, 0, iTarget); | |
| 407 | - pDesc->mergeUpto = pRow->idx; | |
| 408 | - mask = 1<<pDesc->mergeOut; | |
| 409 | - for(pLoop=pRow->pNext; pLoop && pLoop->rid!=parentRid; | |
| 410 | - pLoop=pLoop->pNext){ | |
| 389 | + if( pDesc==0 ){ | |
| 390 | + /* Merge from a node that is off-screen */ | |
| 391 | + int iMrail = findFreeRail(p, pRow->idx, p->nRow, 0, 0); | |
| 392 | + mask = 1<<iMrail; | |
| 393 | + pRow->mergeIn |= mask; | |
| 394 | + pRow->mergeDown |= mask; | |
| 395 | + for(pLoop=pRow->pNext; pLoop; pLoop=pLoop->pNext){ | |
| 411 | 396 | pLoop->railInUse |= mask; |
| 412 | 397 | } |
| 398 | + }else{ | |
| 399 | + /* Merge from an on-screen node */ | |
| 400 | + if( pDesc->mergeOut<0 ){ | |
| 401 | + int iTarget = (pRow->iRail + pDesc->iRail)/2; | |
| 402 | + pDesc->mergeOut = findFreeRail(p, pRow->idx, pDesc->idx-1,0,iTarget); | |
| 403 | + pDesc->mergeUpto = pRow->idx; | |
| 404 | + mask = 1<<pDesc->mergeOut; | |
| 405 | + for(pLoop=pRow->pNext; pLoop && pLoop->rid!=parentRid; | |
| 406 | + pLoop=pLoop->pNext){ | |
| 407 | + pLoop->railInUse |= mask; | |
| 408 | + } | |
| 409 | + } | |
| 410 | + pRow->mergeIn |= 1<<pDesc->mergeOut; | |
| 413 | 411 | } |
| 414 | - pRow->mergeIn |= 1<<pDesc->mergeOut; | |
| 415 | 412 | } |
| 416 | 413 | } |
| 417 | 414 | |
| 418 | 415 | /* |
| 419 | 416 | ** Insert merge rails from primaries to duplicates. |
| @@ -441,7 +438,8 @@ | ||
| 441 | 438 | */ |
| 442 | 439 | p->mxRail = 0; |
| 443 | 440 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 444 | 441 | if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail; |
| 445 | 442 | if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut; |
| 443 | + while( pRow->mergeDown>((1<<(p->mxRail+1))-1) ) p->mxRail++; | |
| 446 | 444 | } |
| 447 | 445 | } |
| 448 | 446 |
| --- src/graph.c | |
| +++ src/graph.c | |
| @@ -21,15 +21,17 @@ | |
| 21 | #include "graph.h" |
| 22 | #include <assert.h> |
| 23 | |
| 24 | #if INTERFACE |
| 25 | |
| 26 | #define GR_MAX_PARENT 10 /* Most parents for any one node */ |
| 27 | #define GR_MAX_RAIL 32 /* Max number of "rails" to display */ |
| 28 | |
| 29 | /* The graph appears vertically beside a timeline. Each row in the |
| 30 | ** timeline corresponds to a row in the graph. |
| 31 | */ |
| 32 | struct GraphRow { |
| 33 | int rid; /* The rid for the check-in */ |
| 34 | int nParent; /* Number of parents */ |
| 35 | int aParent[GR_MAX_PARENT]; /* Array of parents. 0 element is primary .*/ |
| @@ -47,10 +49,11 @@ | |
| 47 | int aiRaiser[GR_MAX_RAIL]; /* Raisers from this node to a higher row. */ |
| 48 | int bDescender; /* Raiser from bottom of graph to here. */ |
| 49 | u32 mergeIn; /* Merge in from other rails */ |
| 50 | int mergeOut; /* Merge out to this rail */ |
| 51 | int mergeUpto; /* Draw the merge rail up to this level */ |
| 52 | |
| 53 | u32 railInUse; /* Mask of occupied rails */ |
| 54 | }; |
| 55 | |
| 56 | /* Context while building a graph |
| @@ -277,27 +280,10 @@ | |
| 277 | } |
| 278 | hashInsert(p, pRow, 1); |
| 279 | } |
| 280 | p->mxRail = -1; |
| 281 | |
| 282 | /* Purge merge-parents that are out-of-graph. |
| 283 | ** |
| 284 | ** Each node has one primary parent and zero or more "merge" parents. |
| 285 | ** A merge parent is a prior checkin from which changes were merged into |
| 286 | ** the current check-in. If a merge parent is not in the visible section |
| 287 | ** of this graph, then no arrows will be drawn for it, so remove it from |
| 288 | ** the aParent[] array. |
| 289 | */ |
| 290 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 291 | for(i=1; i<pRow->nParent; i++){ |
| 292 | if( hashFind(p, pRow->aParent[i])==0 ){ |
| 293 | pRow->aParent[i] = pRow->aParent[--pRow->nParent]; |
| 294 | i--; |
| 295 | } |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | /* Find the pChild pointer for each node. |
| 300 | ** |
| 301 | ** The pChild points to the node directly above on the same rail. |
| 302 | ** The pChild must be in the same branch. Leaf nodes have a NULL |
| 303 | ** pChild. |
| @@ -398,22 +384,33 @@ | |
| 398 | */ |
| 399 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 400 | for(i=1; i<pRow->nParent; i++){ |
| 401 | int parentRid = pRow->aParent[i]; |
| 402 | pDesc = hashFind(p, parentRid); |
| 403 | if( pDesc==0 ) continue; |
| 404 | if( pDesc->mergeOut<0 ){ |
| 405 | int iTarget = (pRow->iRail + pDesc->iRail)/2; |
| 406 | pDesc->mergeOut = findFreeRail(p, pRow->idx, pDesc->idx-1, 0, iTarget); |
| 407 | pDesc->mergeUpto = pRow->idx; |
| 408 | mask = 1<<pDesc->mergeOut; |
| 409 | for(pLoop=pRow->pNext; pLoop && pLoop->rid!=parentRid; |
| 410 | pLoop=pLoop->pNext){ |
| 411 | pLoop->railInUse |= mask; |
| 412 | } |
| 413 | } |
| 414 | pRow->mergeIn |= 1<<pDesc->mergeOut; |
| 415 | } |
| 416 | } |
| 417 | |
| 418 | /* |
| 419 | ** Insert merge rails from primaries to duplicates. |
| @@ -441,7 +438,8 @@ | |
| 441 | */ |
| 442 | p->mxRail = 0; |
| 443 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 444 | if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail; |
| 445 | if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut; |
| 446 | } |
| 447 | } |
| 448 |
| --- src/graph.c | |
| +++ src/graph.c | |
| @@ -21,15 +21,17 @@ | |
| 21 | #include "graph.h" |
| 22 | #include <assert.h> |
| 23 | |
| 24 | #if INTERFACE |
| 25 | |
| 26 | #define GR_MAX_PARENT 10 /* Max number of parents for any one node */ |
| 27 | #define GR_MAX_RAIL 32 /* Max number of "rails" to display */ |
| 28 | |
| 29 | /* The graph appears vertically beside a timeline. Each row in the |
| 30 | ** timeline corresponds to a row in the graph. GraphRow.idx is 0 for |
| 31 | ** the top-most row and increases moving down. Hence (in the absence of |
| 32 | ** time skew) parents have a larger index than their children. |
| 33 | */ |
| 34 | struct GraphRow { |
| 35 | int rid; /* The rid for the check-in */ |
| 36 | int nParent; /* Number of parents */ |
| 37 | int aParent[GR_MAX_PARENT]; /* Array of parents. 0 element is primary .*/ |
| @@ -47,10 +49,11 @@ | |
| 49 | int aiRaiser[GR_MAX_RAIL]; /* Raisers from this node to a higher row. */ |
| 50 | int bDescender; /* Raiser from bottom of graph to here. */ |
| 51 | u32 mergeIn; /* Merge in from other rails */ |
| 52 | int mergeOut; /* Merge out to this rail */ |
| 53 | int mergeUpto; /* Draw the merge rail up to this level */ |
| 54 | u32 mergeDown; /* Draw merge lines up from bottom of graph */ |
| 55 | |
| 56 | u32 railInUse; /* Mask of occupied rails */ |
| 57 | }; |
| 58 | |
| 59 | /* Context while building a graph |
| @@ -277,27 +280,10 @@ | |
| 280 | } |
| 281 | hashInsert(p, pRow, 1); |
| 282 | } |
| 283 | p->mxRail = -1; |
| 284 | |
| 285 | /* Find the pChild pointer for each node. |
| 286 | ** |
| 287 | ** The pChild points to the node directly above on the same rail. |
| 288 | ** The pChild must be in the same branch. Leaf nodes have a NULL |
| 289 | ** pChild. |
| @@ -398,22 +384,33 @@ | |
| 384 | */ |
| 385 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 386 | for(i=1; i<pRow->nParent; i++){ |
| 387 | int parentRid = pRow->aParent[i]; |
| 388 | pDesc = hashFind(p, parentRid); |
| 389 | if( pDesc==0 ){ |
| 390 | /* Merge from a node that is off-screen */ |
| 391 | int iMrail = findFreeRail(p, pRow->idx, p->nRow, 0, 0); |
| 392 | mask = 1<<iMrail; |
| 393 | pRow->mergeIn |= mask; |
| 394 | pRow->mergeDown |= mask; |
| 395 | for(pLoop=pRow->pNext; pLoop; pLoop=pLoop->pNext){ |
| 396 | pLoop->railInUse |= mask; |
| 397 | } |
| 398 | }else{ |
| 399 | /* Merge from an on-screen node */ |
| 400 | if( pDesc->mergeOut<0 ){ |
| 401 | int iTarget = (pRow->iRail + pDesc->iRail)/2; |
| 402 | pDesc->mergeOut = findFreeRail(p, pRow->idx, pDesc->idx-1,0,iTarget); |
| 403 | pDesc->mergeUpto = pRow->idx; |
| 404 | mask = 1<<pDesc->mergeOut; |
| 405 | for(pLoop=pRow->pNext; pLoop && pLoop->rid!=parentRid; |
| 406 | pLoop=pLoop->pNext){ |
| 407 | pLoop->railInUse |= mask; |
| 408 | } |
| 409 | } |
| 410 | pRow->mergeIn |= 1<<pDesc->mergeOut; |
| 411 | } |
| 412 | } |
| 413 | } |
| 414 | |
| 415 | /* |
| 416 | ** Insert merge rails from primaries to duplicates. |
| @@ -441,7 +438,8 @@ | |
| 438 | */ |
| 439 | p->mxRail = 0; |
| 440 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 441 | if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail; |
| 442 | if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut; |
| 443 | while( pRow->mergeDown>((1<<(p->mxRail+1))-1) ) p->mxRail++; |
| 444 | } |
| 445 | } |
| 446 |
+5
-1
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -352,17 +352,18 @@ | ||
| 352 | 352 | char cSep; |
| 353 | 353 | @ <script type="text/JavaScript"> |
| 354 | 354 | @ /* <![CDATA[ */ |
| 355 | 355 | cgi_printf("var rowinfo = [\n"); |
| 356 | 356 | for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){ |
| 357 | - cgi_printf("{id:\"m%d\",bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,au:", | |
| 357 | + cgi_printf("{id:\"m%d\",bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,md:%u,u:%d,au:", | |
| 358 | 358 | pRow->idx, |
| 359 | 359 | pRow->zBgClr, |
| 360 | 360 | pRow->iRail, |
| 361 | 361 | pRow->bDescender, |
| 362 | 362 | pRow->mergeOut, |
| 363 | 363 | pRow->mergeUpto, |
| 364 | + pRow->mergeDown, | |
| 364 | 365 | pRow->aiRaiser[pRow->iRail] |
| 365 | 366 | ); |
| 366 | 367 | cSep = '['; |
| 367 | 368 | for(i=0; i<GR_MAX_RAIL; i++){ |
| 368 | 369 | if( i==pRow->iRail ) continue; |
| @@ -485,10 +486,13 @@ | ||
| 485 | 486 | @ if( mx>p.x ){ |
| 486 | 487 | @ drawThinArrow(y0,mx,p.x+6); |
| 487 | 488 | @ }else{ |
| 488 | 489 | @ drawThinArrow(y0,mx,p.x-5); |
| 489 | 490 | @ } |
| 491 | + @ if( (1<<p.mi[j])&p.md ){ | |
| 492 | + @ drawThinLine(mx,y0,mx,btm); | |
| 493 | + @ } | |
| 490 | 494 | @ } |
| 491 | 495 | @ } |
| 492 | 496 | @ function renderGraph(){ |
| 493 | 497 | @ var canvasDiv = document.getElementById("canvas"); |
| 494 | 498 | @ while( canvasDiv.hasChildNodes() ){ |
| 495 | 499 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -352,17 +352,18 @@ | |
| 352 | char cSep; |
| 353 | @ <script type="text/JavaScript"> |
| 354 | @ /* <![CDATA[ */ |
| 355 | cgi_printf("var rowinfo = [\n"); |
| 356 | for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){ |
| 357 | cgi_printf("{id:\"m%d\",bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,au:", |
| 358 | pRow->idx, |
| 359 | pRow->zBgClr, |
| 360 | pRow->iRail, |
| 361 | pRow->bDescender, |
| 362 | pRow->mergeOut, |
| 363 | pRow->mergeUpto, |
| 364 | pRow->aiRaiser[pRow->iRail] |
| 365 | ); |
| 366 | cSep = '['; |
| 367 | for(i=0; i<GR_MAX_RAIL; i++){ |
| 368 | if( i==pRow->iRail ) continue; |
| @@ -485,10 +486,13 @@ | |
| 485 | @ if( mx>p.x ){ |
| 486 | @ drawThinArrow(y0,mx,p.x+6); |
| 487 | @ }else{ |
| 488 | @ drawThinArrow(y0,mx,p.x-5); |
| 489 | @ } |
| 490 | @ } |
| 491 | @ } |
| 492 | @ function renderGraph(){ |
| 493 | @ var canvasDiv = document.getElementById("canvas"); |
| 494 | @ while( canvasDiv.hasChildNodes() ){ |
| 495 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -352,17 +352,18 @@ | |
| 352 | char cSep; |
| 353 | @ <script type="text/JavaScript"> |
| 354 | @ /* <![CDATA[ */ |
| 355 | cgi_printf("var rowinfo = [\n"); |
| 356 | for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){ |
| 357 | cgi_printf("{id:\"m%d\",bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,md:%u,u:%d,au:", |
| 358 | pRow->idx, |
| 359 | pRow->zBgClr, |
| 360 | pRow->iRail, |
| 361 | pRow->bDescender, |
| 362 | pRow->mergeOut, |
| 363 | pRow->mergeUpto, |
| 364 | pRow->mergeDown, |
| 365 | pRow->aiRaiser[pRow->iRail] |
| 366 | ); |
| 367 | cSep = '['; |
| 368 | for(i=0; i<GR_MAX_RAIL; i++){ |
| 369 | if( i==pRow->iRail ) continue; |
| @@ -485,10 +486,13 @@ | |
| 486 | @ if( mx>p.x ){ |
| 487 | @ drawThinArrow(y0,mx,p.x+6); |
| 488 | @ }else{ |
| 489 | @ drawThinArrow(y0,mx,p.x-5); |
| 490 | @ } |
| 491 | @ if( (1<<p.mi[j])&p.md ){ |
| 492 | @ drawThinLine(mx,y0,mx,btm); |
| 493 | @ } |
| 494 | @ } |
| 495 | @ } |
| 496 | @ function renderGraph(){ |
| 497 | @ var canvasDiv = document.getElementById("canvas"); |
| 498 | @ while( canvasDiv.hasChildNodes() ){ |
| 499 |