Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Change the graph layout to include ascenders for nodes that have off-screen children. Fix a bug in the leaf-tracker that became evident by this enhancement. (A rebuild is required to rebuild the leaf table after updating, or else some timeline graphs will not display correctly.) |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA1: |
d7a0240f3963be22136155b857d52a60 |
| User & Date: | drh 2011-03-15 01:34:04.426 |
Context
|
2011-03-15
| ||
| 01:44 | A merge riser is allowed to ride along side a riser to an off-screen node. ... (check-in: 52ea66df56 user: drh tags: trunk) | |
| 01:34 | Change the graph layout to include ascenders for nodes that have off-screen children. Fix a bug in the leaf-tracker that became evident by this enhancement. (A rebuild is required to rebuild the leaf table after updating, or else some timeline graphs will not display correctly.) ... (check-in: d7a0240f39 user: drh tags: trunk) | |
|
2011-03-14
| ||
| 21:36 | Fix an issue with the leaf-tracker that comes up if a check-in specifies a shunned check-in as its parent or merge parent. Ticket [398afa58def78f5]. ... (check-in: aa6abc5ed3 user: drh tags: trunk) | |
Changes
Changes to src/finfo.c.
| ︙ | ︙ | |||
244 245 246 247 248 249 250 |
const char *zBgClr = db_column_text(&q, 8);
const char *zBr = db_column_text(&q, 9);
int gidx;
char zTime[10];
char zShort[20];
char zShortCkin[20];
if( zBr==0 ) zBr = "trunk";
| | | 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
const char *zBgClr = db_column_text(&q, 8);
const char *zBr = db_column_text(&q, 9);
int gidx;
char zTime[10];
char zShort[20];
char zShortCkin[20];
if( zBr==0 ) zBr = "trunk";
gidx = graph_add_row(pGraph, frid, fpid>0 ? 1 : 0, &fpid, zBr, zBgClr, 1);
if( memcmp(zDate, zPrevDate, 10) ){
sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);
@ <tr><td>
@ <div class="divider">%s(zPrevDate)</div>
@ </td></tr>
}
memcpy(zTime, &zDate[11], 5);
|
| ︙ | ︙ | |||
299 300 301 302 303 304 305 |
pGraph = 0;
}else{
@ <tr><td></td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
@ </td></tr>
}
}
@ </table>
| | | 299 300 301 302 303 304 305 306 307 308 |
pGraph = 0;
}else{
@ <tr><td></td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
@ </td></tr>
}
}
@ </table>
timeline_output_graph_javascript(pGraph, 1);
style_footer();
}
|
Changes to src/graph.c.
| ︙ | ︙ | |||
40 41 42 43 44 45 46 47 48 49 50 51 52 53 | GraphRow *pNext; /* Next row down in the list of all rows */ GraphRow *pPrev; /* Previous row */ int idx; /* Row index. First is 1. 0 used for "none" */ int idxTop; /* Direct descendent highest up on the graph */ GraphRow *pChild; /* Child immediately above this node */ u8 isDup; /* True if this is duplicate of a prior entry */ u8 bDescender; /* True if riser from bottom of graph to here. */ i8 iRail; /* Which rail this check-in appears on. 0-based.*/ i8 mergeOut; /* Merge out to this rail. -1 if no merge-out */ u8 mergeIn[GR_MAX_RAIL]; /* Merge in from non-zero rails */ int aiRiser[GR_MAX_RAIL]; /* Risers from this node to a higher row. */ int mergeUpto; /* Draw the mergeOut rail up to this level */ u32 mergeDown; /* Draw merge lines up from bottom of graph */ | > > | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | GraphRow *pNext; /* Next row down in the list of all rows */ GraphRow *pPrev; /* Previous row */ int idx; /* Row index. First is 1. 0 used for "none" */ int idxTop; /* Direct descendent highest up on the graph */ GraphRow *pChild; /* Child immediately above this node */ u8 isDup; /* True if this is duplicate of a prior entry */ u8 isLeaf; /* True if this is a leaf node */ u8 timeWarp; /* Child is earlier in time */ u8 bDescender; /* True if riser from bottom of graph to here. */ i8 iRail; /* Which rail this check-in appears on. 0-based.*/ i8 mergeOut; /* Merge out to this rail. -1 if no merge-out */ u8 mergeIn[GR_MAX_RAIL]; /* Merge in from non-zero rails */ int aiRiser[GR_MAX_RAIL]; /* Risers from this node to a higher row. */ int mergeUpto; /* Draw the mergeOut rail up to this level */ u32 mergeDown; /* Draw merge lines up from bottom of graph */ |
| ︙ | ︙ | |||
161 162 163 164 165 166 167 | */ int graph_add_row( GraphContext *p, /* The context to which the row is added */ int rid, /* RID for the check-in */ int nParent, /* Number of parents */ int *aParent, /* Array of parents */ const char *zBranch, /* Branch for this check-in */ | | > > > | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
*/
int graph_add_row(
GraphContext *p, /* The context to which the row is added */
int rid, /* RID for the check-in */
int nParent, /* Number of parents */
int *aParent, /* Array of parents */
const char *zBranch, /* Branch for this check-in */
const char *zBgClr, /* Background color. NULL or "" for white. */
int isLeaf /* True if this row is a leaf */
){
GraphRow *pRow;
int nByte;
if( p->nErr ) return 0;
nByte = sizeof(GraphRow);
nByte += sizeof(pRow->aParent[0])*nParent;
pRow = (GraphRow*)safeMalloc( nByte );
pRow->aParent = (int*)&pRow[1];
pRow->rid = rid;
pRow->nParent = nParent;
pRow->zBranch = persistBranchName(p, zBranch);
pRow->isLeaf = isLeaf;
memset(pRow->aiRiser, -1, sizeof(pRow->aiRiser));
if( zBgClr==0 || zBgClr[0]==0 ) zBgClr = "white";
pRow->zBgClr = persistBranchName(p, zBgClr);
memcpy(pRow->aParent, aParent, sizeof(aParent[0])*nParent);
if( p->pFirst==0 ){
p->pFirst = pRow;
}else{
p->pLast->pNext = pRow;
|
| ︙ | ︙ | |||
222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
if( dist<iBestDist ){
iBestDist = dist;
iBest = i;
}
}
}
if( iBestDist>1000 ) p->nErr++;
return iBest;
}
/*
** Assign all children of node pBottom to the same rail as pBottom.
*/
static void assignChildrenToRail(GraphRow *pBottom){
| > | 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
if( dist<iBestDist ){
iBestDist = dist;
iBest = i;
}
}
}
if( iBestDist>1000 ) p->nErr++;
if( iBest>p->mxRail ) p->mxRail = iBest;
return iBest;
}
/*
** Assign all children of node pBottom to the same rail as pBottom.
*/
static void assignChildrenToRail(GraphRow *pBottom){
|
| ︙ | ︙ | |||
354 355 356 357 358 359 360 |
*/
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
if( pRow->isDup ) continue;
if( pRow->nParent==0 ) continue; /* Root node */
pParent = hashFind(p, pRow->aParent[0]);
if( pParent==0 ) continue; /* Parent off-screen */
if( pParent->zBranch!=pRow->zBranch ) continue; /* Different branch */
| | > > > | 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
*/
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
if( pRow->isDup ) continue;
if( pRow->nParent==0 ) continue; /* Root node */
pParent = hashFind(p, pRow->aParent[0]);
if( pParent==0 ) continue; /* Parent off-screen */
if( pParent->zBranch!=pRow->zBranch ) continue; /* Different branch */
if( pParent->idx <= pRow->idx ){
pParent->timeWarp = 1;
continue; /* Time-warp */
}
if( pRow->idxTop < pParent->idxTop ){
pParent->pChild = pRow;
pParent->idxTop = pRow->idxTop;
}
}
/* Identify rows where the primary parent is off screen. Assign
|
| ︙ | ︙ | |||
399 400 401 402 403 404 405 |
/* Assign rails to all rows that are still unassigned.
*/
inUse = (1<<(p->mxRail+1))-1;
for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
int parentRid;
if( pRow->iRail>=0 ){
| > > | > > > > > > > > | 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 |
/* Assign rails to all rows that are still unassigned.
*/
inUse = (1<<(p->mxRail+1))-1;
for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
int parentRid;
if( pRow->iRail>=0 ){
if( pRow->pChild==0 && !pRow->timeWarp ){
if( pRow->isLeaf || omitDescenders ){
inUse &= ~(1<<pRow->iRail);
}else{
pRow->aiRiser[pRow->iRail] = 0;
mask = 1<<pRow->iRail;
for(pLoop=pRow; pLoop; pLoop=pLoop->pPrev){
pLoop->railInUse |= mask;
}
}
}
continue;
}
if( pRow->isDup ){
pRow->iRail = findFreeRail(p, pRow->idx, pRow->idx, inUse, 0);
pDesc = pRow;
pParent = 0;
}else{
|
| ︙ | ︙ | |||
429 430 431 432 433 434 435 436 437 438 439 440 441 442 |
** appears below the parent in the timeline. */
int iDownRail = ++p->mxRail;
if( iDownRail<1 ) iDownRail = ++p->mxRail;
pRow->iRail = ++p->mxRail;
pRow->railInUse = 1<<pRow->iRail;
pParent->aiRiser[iDownRail] = pRow->idx;
mask = 1<<iDownRail;
for(pLoop=p->pFirst; pLoop; pLoop=pLoop->pNext){
pLoop->railInUse |= mask;
}
}
}
mask = 1<<pRow->iRail;
pRow->railInUse |= mask;
| > | 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 |
** appears below the parent in the timeline. */
int iDownRail = ++p->mxRail;
if( iDownRail<1 ) iDownRail = ++p->mxRail;
pRow->iRail = ++p->mxRail;
pRow->railInUse = 1<<pRow->iRail;
pParent->aiRiser[iDownRail] = pRow->idx;
mask = 1<<iDownRail;
inUse |= mask;
for(pLoop=p->pFirst; pLoop; pLoop=pLoop->pNext){
pLoop->railInUse |= mask;
}
}
}
mask = 1<<pRow->iRail;
pRow->railInUse |= mask;
|
| ︙ | ︙ |
Changes to src/leaf.c.
| ︙ | ︙ | |||
38 39 40 41 42 43 44 |
"INSERT OR IGNORE INTO leaf"
" SELECT cid FROM plink"
" EXCEPT"
" SELECT pid FROM plink"
" WHERE coalesce((SELECT value FROM tagxref"
" WHERE tagid=%d AND rid=plink.pid),'trunk')"
" == coalesce((SELECT value FROM tagxref"
| | > | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
"INSERT OR IGNORE INTO leaf"
" SELECT cid FROM plink"
" EXCEPT"
" SELECT pid FROM plink"
" WHERE coalesce((SELECT value FROM tagxref"
" WHERE tagid=%d AND rid=plink.pid),'trunk')"
" == coalesce((SELECT value FROM tagxref"
" WHERE tagid=%d AND rid=plink.cid),'trunk')"
" AND isprim",
TAG_BRANCH, TAG_BRANCH
);
}
/*
** A bag of checkins whose leaf status needs to be checked.
*/
|
| ︙ | ︙ | |||
60 61 62 63 64 65 66 |
static Stmt checkIfLeaf;
static Stmt addLeaf;
static Stmt removeLeaf;
int rc;
db_static_prepare(&checkIfLeaf,
"SELECT 1 FROM plink"
| | | 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
static Stmt checkIfLeaf;
static Stmt addLeaf;
static Stmt removeLeaf;
int rc;
db_static_prepare(&checkIfLeaf,
"SELECT 1 FROM plink"
" WHERE pid=:rid AND isprim"
" AND coalesce((SELECT value FROM tagxref"
" WHERE tagid=%d AND rid=:rid),'trunk')"
" == coalesce((SELECT value FROM tagxref"
" WHERE tagid=%d AND rid=plink.cid),'trunk');",
TAG_BRANCH, TAG_BRANCH
);
db_bind_int(&checkIfLeaf, ":rid", rid);
|
| ︙ | ︙ |
Changes to src/timeline.c.
| ︙ | ︙ | |||
252 253 254 255 256 257 258 |
db_reset(&qparent);
db_bind_int(&qbranch, ":rid", rid);
if( db_step(&qbranch)==SQLITE_ROW ){
zBr = db_column_text(&qbranch, 0);
}else{
zBr = "trunk";
}
| | | 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
db_reset(&qparent);
db_bind_int(&qbranch, ":rid", rid);
if( db_step(&qbranch)==SQLITE_ROW ){
zBr = db_column_text(&qbranch, 0);
}else{
zBr = "trunk";
}
gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr, isLeaf);
db_reset(&qbranch);
@ <div id="m%d(gidx)"></div>
}
@</td>
if( zBgClr && zBgClr[0] ){
@ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
}else{
|
| ︙ | ︙ | |||
360 361 362 363 364 365 366 |
*/
@ <tr><td /><td>
@ <div id="grbtm" style="width:%d(pGraph->mxRail*20+30)px;"></div>
@ </td></tr>
}
}
@ </table>
| | | | 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 |
*/
@ <tr><td /><td>
@ <div id="grbtm" style="width:%d(pGraph->mxRail*20+30)px;"></div>
@ </td></tr>
}
}
@ </table>
timeline_output_graph_javascript(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0);
}
/*
** Generate all of the necessary javascript to generate a timeline
** graph.
*/
void timeline_output_graph_javascript(GraphContext *pGraph, int omitDescenders){
if( pGraph && pGraph->nErr==0 ){
GraphRow *pRow;
int i;
char cSep;
@ <script type="text/JavaScript">
@ /* <![CDATA[ */
|
| ︙ | ︙ | |||
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 |
** for the upward portion of a merge arrow. The merge arrow goes up
** to the row identified by mu:. If this value is zero then
** node has no merge children and no merge-out line is drawn.
** mu: The id of the row which is the top of the merge-out arrow.
** u: Draw a thick child-line out of the top of this node and up to
** the node with an id equal to this value. 0 if there is no
** thick-line riser.
** au: An array of integers that define thick-line risers for branches.
** The integers are in pairs. For each pair, the first integer is
** is the rail on which the riser should run and the second integer
** is the id of the node upto which the riser should run.
** mi: "merge-in". An array of integer x-coordinates from which
** merge arrows should be drawn into this node. If the value is
** negative, then the x-coordinate is the absolute value of mi[]
** and a thin merge-arrow descender is drawn to the bottom of
** the screen.
*/
cgi_printf("var rowinfo = [\n");
for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
int mo = pRow->mergeOut;
if( mo<0 ){
mo = 0;
}else{
mo = (mo/4)*20 - 3 + 4*(mo&3);
}
| > | | > | 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 |
** for the upward portion of a merge arrow. The merge arrow goes up
** to the row identified by mu:. If this value is zero then
** node has no merge children and no merge-out line is drawn.
** mu: The id of the row which is the top of the merge-out arrow.
** u: Draw a thick child-line out of the top of this node and up to
** the node with an id equal to this value. 0 if there is no
** thick-line riser.
** f: 0x01: a leaf node.
** au: An array of integers that define thick-line risers for branches.
** The integers are in pairs. For each pair, the first integer is
** is the rail on which the riser should run and the second integer
** is the id of the node upto which the riser should run.
** mi: "merge-in". An array of integer x-coordinates from which
** merge arrows should be drawn into this node. If the value is
** negative, then the x-coordinate is the absolute value of mi[]
** and a thin merge-arrow descender is drawn to the bottom of
** the screen.
*/
cgi_printf("var rowinfo = [\n");
for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
int mo = pRow->mergeOut;
if( mo<0 ){
mo = 0;
}else{
mo = (mo/4)*20 - 3 + 4*(mo&3);
}
cgi_printf("{id:%d,bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,f:%d,au:",
pRow->idx, /* id */
pRow->zBgClr, /* bg */
pRow->iRail, /* r */
pRow->bDescender, /* d */
mo, /* mo */
pRow->mergeUpto, /* mu */
pRow->aiRiser[pRow->iRail], /* u */
pRow->isLeaf ? 1 : 0 /* f */
);
/* u */
cSep = '[';
for(i=0; i<GR_MAX_RAIL; i++){
if( i==pRow->iRail ) continue;
if( pRow->aiRiser[i]>0 ){
cgi_printf("%c%d,%d", cSep, i, pRow->aiRiser[i]);
|
| ︙ | ︙ | |||
513 514 515 516 517 518 519 |
@ }
@ function drawThinLine(x0,y0,x1,y1){
@ drawBox("black",x0,y0,x1,y1);
@ }
@ function drawNode(p, left, btm){
@ drawBox("black",p.x-5,p.y-5,p.x+6,p.y+6);
@ drawBox(p.bg,p.x-4,p.y-4,p.x+5,p.y+5);
| | | | < | | < > | 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 |
@ }
@ function drawThinLine(x0,y0,x1,y1){
@ drawBox("black",x0,y0,x1,y1);
@ }
@ function drawNode(p, left, btm){
@ drawBox("black",p.x-5,p.y-5,p.x+6,p.y+6);
@ drawBox(p.bg,p.x-4,p.y-4,p.x+5,p.y+5);
@ if( p.u>0 ) drawUpArrow(p.x, rowinfo[p.u-1].y+6, p.y-5);
if( !omitDescenders ){
@ if( p.u==0 ) drawUpArrow(p.x, 0, p.y-5);
@ if( p.f&1 ) drawBox("black",p.x-1,p.y-1,p.x+2,p.y+2);
@ if( p.d ) drawUpArrow(p.x, p.y+6, btm);
}
@ if( p.mo>0 ){
@ var x1 = p.mo + left - 1;
@ var y1 = p.y-3;
@ var x0 = x1>p.x ? p.x+7 : p.x-6;
@ var u = rowinfo[p.mu-1];
@ var y0 = u.y+5;
@ if( x1>=p.x-5 && x1<=p.x+5 ){
|
| ︙ | ︙ |