Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Click on the timeline graph once to select a node. Click on a different node to see a diff between the two. Click on the selected node to undo the selection. Works with check-in timelines and file timelines. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | click-graph-to-diff |
| Files: | files | file ages | folders |
| SHA1: |
a7dde41fec628655c7c0d60c00e5d45e |
| User & Date: | drh 2012-11-30 03:10:24.712 |
Context
|
2012-11-30
| ||
| 03:14 | Fix compiler warning. check-in: 95cce92af0 user: drh tags: click-graph-to-diff | |
| 03:10 | Click on the timeline graph once to select a node. Click on a different node to see a diff between the two. Click on the selected node to undo the selection. Works with check-in timelines and file timelines. check-in: a7dde41fec user: drh tags: click-graph-to-diff | |
| 00:40 | Move the new utf8.c file into alphabetical order in the makemake.tcl tool. check-in: 3e0f48c76d user: mistachkin tags: trunk | |
Changes
Changes to src/finfo.c.
| ︙ | ︙ | |||
343 344 345 346 347 348 349 | blob_reset(&sql); blob_zero(&title); blob_appendf(&title, "History of "); hyperlinked_path(zFilename, &title, 0); @ <h2>%b(&title)</h2> blob_reset(&title); pGraph = graph_init(); | | > | 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
blob_reset(&sql);
blob_zero(&title);
blob_appendf(&title, "History of ");
hyperlinked_path(zFilename, &title, 0);
@ <h2>%b(&title)</h2>
blob_reset(&title);
pGraph = graph_init();
@ <div id="canvas" style="position:relative;width:1px;height:1px;"
@ onclick="clickOnGraph(event)"></div>
@ <table id="timelineTable" class="timelineTable">
while( db_step(&q)==SQLITE_ROW ){
const char *zDate = db_column_text(&q, 0);
const char *zCom = db_column_text(&q, 1);
const char *zUser = db_column_text(&q, 2);
int fpid = db_column_int(&q, 3);
int frid = db_column_int(&q, 4);
|
| ︙ | ︙ | |||
368 369 370 371 372 373 374 |
char zShortCkin[20];
if( zBr==0 ) zBr = "trunk";
if( uBg ){
zBgClr = hash_color(zUser);
}else if( brBg || zBgClr==0 || zBgClr[0]==0 ){
zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr);
}
| | > | 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 |
char zShortCkin[20];
if( zBr==0 ) zBr = "trunk";
if( uBg ){
zBgClr = hash_color(zUser);
}else if( brBg || zBgClr==0 || zBgClr[0]==0 ){
zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr);
}
gidx = graph_add_row(pGraph, frid, fpid>0 ? 1 : 0, &fpid, zBr, zBgClr,
zUuid, 0);
if( memcmp(zDate, zPrevDate, 10) ){
sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);
@ <tr><td>
@ <div class="divider">%s(zPrevDate)</div>
@ </td><td></td><td></td></tr>
}
memcpy(zTime, &zDate[11], 5);
|
| ︙ | ︙ | |||
444 445 446 447 448 449 450 |
}else{
@ <tr><td></td><td>
@ <div id="grbtm" style="width:%d(pGraph->mxRail*20+30)px;"></div>
@ </td><td></td></tr>
}
}
@ </table>
| | | 446 447 448 449 450 451 452 453 454 455 |
}else{
@ <tr><td></td><td>
@ <div id="grbtm" style="width:%d(pGraph->mxRail*20+30)px;"></div>
@ </td><td></td></tr>
}
}
@ </table>
timeline_output_graph_javascript(pGraph, 0, 1);
style_footer();
}
|
Changes to src/graph.c.
| ︙ | ︙ | |||
32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
*/
struct GraphRow {
int rid; /* The rid for the check-in */
i8 nParent; /* Number of parents */
int *aParent; /* Array of parents. 0 element is primary .*/
char *zBranch; /* Branch name */
char *zBgClr; /* Background Color */
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 */
| > | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
*/
struct GraphRow {
int rid; /* The rid for the check-in */
i8 nParent; /* Number of parents */
int *aParent; /* Array of parents. 0 element is primary .*/
char *zBranch; /* Branch name */
char *zBgClr; /* Background Color */
char zUuid[17]; /* Check-in for file ID */
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 */
|
| ︙ | ︙ | |||
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
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;
| > > > | 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
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. */
const char *zUuid, /* SHA1 hash of the object being graphed */
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);
if( zUuid==0 ) zUuid=="";
sqlite3_snprintf(sizeof(pRow->zUuid), pRow->zUuid, "%s", zUuid);
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;
|
| ︙ | ︙ |
Changes to src/timeline.c.
| ︙ | ︙ | |||
209 210 211 212 213 214 215 |
zPrevDate[0] = 0;
mxWikiLen = db_get_int("timeline-max-comment", 0);
if( tmFlags & TIMELINE_GRAPH ){
pGraph = graph_init();
/* style is not moved to css, because this is
** a technical div for the timeline graph
*/
| | > | 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
zPrevDate[0] = 0;
mxWikiLen = db_get_int("timeline-max-comment", 0);
if( tmFlags & TIMELINE_GRAPH ){
pGraph = graph_init();
/* style is not moved to css, because this is
** a technical div for the timeline graph
*/
@ <div id="canvas" style="position:relative;width:1px;height:1px;"
@ onclick="clickOnGraph(event)"></div>
}
db_static_prepare(&qbranch,
"SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
TAG_BRANCH
);
@ <table id="timelineTable" class="timelineTable">
|
| ︙ | ︙ | |||
309 310 311 312 313 314 315 |
" ORDER BY isprim DESC /*sort*/"
);
db_bind_int(&qparent, ":rid", rid);
while( db_step(&qparent)==SQLITE_ROW && nParent<32 ){
aParent[nParent++] = db_column_int(&qparent, 0);
}
db_reset(&qparent);
| | > | 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
" ORDER BY isprim DESC /*sort*/"
);
db_bind_int(&qparent, ":rid", rid);
while( db_step(&qparent)==SQLITE_ROW && nParent<32 ){
aParent[nParent++] = db_column_int(&qparent, 0);
}
db_reset(&qparent);
gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr,
zUuid, isLeaf);
db_reset(&qbranch);
@ <div id="m%d(gidx)"></div>
}
@</td>
if( zBgClr && zBgClr[0] ){
@ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
}else{
|
| ︙ | ︙ | |||
489 490 491 492 493 494 495 |
@ <tr><td></td><td>
@ <div id="grbtm" style="width:%d(pGraph->mxRail*20+30)px;"></div>
@ </td><td></td></tr>
}
}
@ </table>
if( fchngQueryInit ) db_finalize(&fchngQuery);
| | | > > > > | 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 |
@ <tr><td></td><td>
@ <div id="grbtm" style="width:%d(pGraph->mxRail*20+30)px;"></div>
@ </td><td></td></tr>
}
}
@ </table>
if( fchngQueryInit ) db_finalize(&fchngQuery);
timeline_output_graph_javascript(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0, 0);
}
/*
** Generate all of the necessary javascript to generate a timeline
** graph.
*/
void timeline_output_graph_javascript(
GraphContext *pGraph, /* The graph to be displayed */
int omitDescenders, /* True to omit descenders */
int fileDiff /* True for file diff. False for check-in diff */
){
if( pGraph && pGraph->nErr==0 && pGraph->nRow>0 ){
GraphRow *pRow;
int i;
char cSep;
@ <script type="text/JavaScript">
@ /* <![CDATA[ */
|
| ︙ | ︙ | |||
533 534 535 536 537 538 539 540 541 542 543 544 545 546 |
** 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{
| > | 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 |
** 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.
** h: The SHA1 hash of the object being graphed
*/
cgi_printf("var rowinfo = [\n");
for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
int mo = pRow->mergeOut;
if( mo<0 ){
mo = 0;
}else{
|
| ︙ | ︙ | |||
574 575 576 577 578 579 580 |
int mi = i*20 - 8 + 4*pRow->mergeIn[i];
if( pRow->mergeDown & (1<<i) ) mi = -mi;
cgi_printf("%c%d", cSep, mi);
cSep = ',';
}
}
if( cSep=='[' ) cgi_printf("[");
| | | 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 |
int mi = i*20 - 8 + 4*pRow->mergeIn[i];
if( pRow->mergeDown & (1<<i) ) mi = -mi;
cgi_printf("%c%d", cSep, mi);
cSep = ',';
}
}
if( cSep=='[' ) cgi_printf("[");
cgi_printf("],h:\"%s\"}%s", pRow->zUuid, pRow->pNext ? ",\n" : "];\n");
}
cgi_printf("var nrail = %d\n", pGraph->mxRail+1);
graph_free(pGraph);
@ var canvasDiv = gebi("canvas");
#if 0
@ var realCanvas = null;
#endif
|
| ︙ | ︙ | |||
596 597 598 599 600 601 602 603 604 605 606 607 608 609 |
@ n.style.overflow = "hidden";
@ n.style.left = x0+"px";
@ n.style.top = y0+"px";
@ n.style.width = w+"px";
@ n.style.height = h+"px";
@ n.style.backgroundColor = color;
@ canvasDiv.appendChild(n);
@ }
@ function absoluteY(id){
@ var obj = gebi(id);
@ if( !obj ) return;
@ var top = 0;
@ if( obj.offsetParent ){
@ do{
| > | 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 |
@ n.style.overflow = "hidden";
@ n.style.left = x0+"px";
@ n.style.top = y0+"px";
@ n.style.width = w+"px";
@ n.style.height = h+"px";
@ n.style.backgroundColor = color;
@ canvasDiv.appendChild(n);
@ return n;
@ }
@ function absoluteY(id){
@ var obj = gebi(id);
@ if( !obj ) return;
@ var top = 0;
@ if( obj.offsetParent ){
@ do{
|
| ︙ | ︙ | |||
697 698 699 700 701 702 703 704 705 706 707 708 709 710 |
@ if( mx>p.x ){
@ drawThinArrow(y0,mx,p.x+6);
@ }else{
@ drawThinArrow(y0,mx,p.x-5);
@ }
@ }
@ }
@ function renderGraph(){
@ var canvasDiv = gebi("canvas");
@ while( canvasDiv.hasChildNodes() ){
@ canvasDiv.removeChild(canvasDiv.firstChild);
@ }
@ var canvasY = absoluteY("timelineTable");
@ var left = absoluteX("m"+rowinfo[0].id) - absoluteX("canvas") + 15;
| > > | 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 |
@ if( mx>p.x ){
@ drawThinArrow(y0,mx,p.x+6);
@ }else{
@ drawThinArrow(y0,mx,p.x-5);
@ }
@ }
@ }
@ var selBox = null;
@ var selRow = null;
@ function renderGraph(){
@ var canvasDiv = gebi("canvas");
@ while( canvasDiv.hasChildNodes() ){
@ canvasDiv.removeChild(canvasDiv.firstChild);
@ }
@ var canvasY = absoluteY("timelineTable");
@ var left = absoluteX("m"+rowinfo[0].id) - absoluteX("canvas") + 15;
|
| ︙ | ︙ | |||
734 735 736 737 738 739 740 741 742 743 744 745 746 747 |
@ context.fillStyle = color;
@ context.fillRect(x0-left+5,y0,x1-x0+1,y1-y0+1);
@ };
@ }
#endif
@ for(var i in rowinfo){
@ drawNode(rowinfo[i], left, btm);
@ }
@ }
@ var lastId = "m"+rowinfo[rowinfo.length-1].id;
@ var lastY = 0;
@ function checkHeight(){
@ var h = absoluteY(lastId);
@ if( h!=lastY ){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 |
@ context.fillStyle = color;
@ context.fillRect(x0-left+5,y0,x1-x0+1,y1-y0+1);
@ };
@ }
#endif
@ for(var i in rowinfo){
@ drawNode(rowinfo[i], left, btm);
@ }
@ if( selRow!=null ) clickOnRow(selRow);
@ }
@ function clickOnGraph(event){
@ var x=event.clientX-absoluteX("canvas");
@ var y=event.clientY-absoluteY("canvas");
@ for(var i in rowinfo){
@ p = rowinfo[i];
@ if( p.y<y-10 ) continue;
@ if( p.y>y+10 ) break;
@ if( p.x>x-10 && p.x<x+10 ){
@ clickOnRow(p);
@ break;
@ }
@ }
@ }
@ function clickOnRow(p){
@ if( selRow==null ){
@ selBox = drawBox("red",p.x-2,p.y-2,p.x+3,p.y+3);
@ selRow = p;
@ }else if( selRow==p ){
@ var canvasDiv = gebi("canvas");
@ canvasDiv.removeChild(selBox);
@ selBox = null;
@ selRow = null;
@ }else{
if( fileDiff ){
@ location.href="%R/fdiff?v1="+selRow.h+"&v2="+p.h;
}else{
@ location.href="%R/vdiff?from="+selRow.h+"&to="+p.h;
}
@ }
@ }
@ var lastId = "m"+rowinfo[rowinfo.length-1].id;
@ var lastY = 0;
@ function checkHeight(){
@ var h = absoluteY(lastId);
@ if( h!=lastY ){
|
| ︙ | ︙ |