Fossil SCM

Fix timeline rendering so that multiple timelines can be drawn on each page without interferring with one another. Move the "Referenced By" section of the /info page upward and rename it as "References". Added the /test-backlink page for showing all pages that contain references.

drh 2017-07-05 12:33 trunk
Commit ba3444327f36442c00d01e1873be12041e1025c8ee10e0d2185e27d172965cdd
+3 -2
--- src/finfo.c
+++ src/finfo.c
@@ -310,10 +310,11 @@
310310
int brBg = P("brbg")!=0;
311311
int uBg = P("ubg")!=0;
312312
int fDebug = atoi(PD("debug","0"));
313313
int fShowId = P("showid")!=0;
314314
Stmt qparent;
315
+ int iTableId = timeline_tableid();
315316
316317
login_check_credentials();
317318
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
318319
style_header("File History");
319320
login_anonymous_available();
@@ -415,11 +416,11 @@
415416
if( fShowId ) blob_appendf(&title, " (%d)", fnid);
416417
}
417418
@ <h2>%b(&title)</h2>
418419
blob_reset(&title);
419420
pGraph = graph_init();
420
- @ <table id="timelineTable" class="timelineTable">
421
+ @ <table id="timelineTable%d(iTableId)" class="timelineTable">
421422
if( baseCheckin ){
422423
db_prepare(&qparent,
423424
"SELECT DISTINCT pid FROM mlink"
424425
" WHERE fid=:fid AND mid=:mid AND pid>0 AND fnid=:fnid"
425426
" AND pmid IN (SELECT rid FROM ancestor)"
@@ -558,11 +559,11 @@
558559
}else{
559560
@ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
560561
}
561562
}
562563
@ </table>
563
- timeline_output_graph_javascript(pGraph, 0, 1);
564
+ timeline_output_graph_javascript(pGraph, 0, iTableId, 1);
564565
style_footer();
565566
}
566567
567568
/*
568569
** WEBPAGE: mlink
569570
--- src/finfo.c
+++ src/finfo.c
@@ -310,10 +310,11 @@
310 int brBg = P("brbg")!=0;
311 int uBg = P("ubg")!=0;
312 int fDebug = atoi(PD("debug","0"));
313 int fShowId = P("showid")!=0;
314 Stmt qparent;
 
315
316 login_check_credentials();
317 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
318 style_header("File History");
319 login_anonymous_available();
@@ -415,11 +416,11 @@
415 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
416 }
417 @ <h2>%b(&title)</h2>
418 blob_reset(&title);
419 pGraph = graph_init();
420 @ <table id="timelineTable" class="timelineTable">
421 if( baseCheckin ){
422 db_prepare(&qparent,
423 "SELECT DISTINCT pid FROM mlink"
424 " WHERE fid=:fid AND mid=:mid AND pid>0 AND fnid=:fnid"
425 " AND pmid IN (SELECT rid FROM ancestor)"
@@ -558,11 +559,11 @@
558 }else{
559 @ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
560 }
561 }
562 @ </table>
563 timeline_output_graph_javascript(pGraph, 0, 1);
564 style_footer();
565 }
566
567 /*
568 ** WEBPAGE: mlink
569
--- src/finfo.c
+++ src/finfo.c
@@ -310,10 +310,11 @@
310 int brBg = P("brbg")!=0;
311 int uBg = P("ubg")!=0;
312 int fDebug = atoi(PD("debug","0"));
313 int fShowId = P("showid")!=0;
314 Stmt qparent;
315 int iTableId = timeline_tableid();
316
317 login_check_credentials();
318 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
319 style_header("File History");
320 login_anonymous_available();
@@ -415,11 +416,11 @@
416 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
417 }
418 @ <h2>%b(&title)</h2>
419 blob_reset(&title);
420 pGraph = graph_init();
421 @ <table id="timelineTable%d(iTableId)" class="timelineTable">
422 if( baseCheckin ){
423 db_prepare(&qparent,
424 "SELECT DISTINCT pid FROM mlink"
425 " WHERE fid=:fid AND mid=:mid AND pid>0 AND fnid=:fnid"
426 " AND pmid IN (SELECT rid FROM ancestor)"
@@ -558,11 +559,11 @@
559 }else{
560 @ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
561 }
562 }
563 @ </table>
564 timeline_output_graph_javascript(pGraph, 0, iTableId, 1);
565 style_footer();
566 }
567
568 /*
569 ** WEBPAGE: mlink
570
+2 -1
--- src/graph.c
+++ src/graph.c
@@ -184,10 +184,11 @@
184184
const char *zUuid, /* hash name of the object being graphed */
185185
int isLeaf /* True if this row is a leaf */
186186
){
187187
GraphRow *pRow;
188188
int nByte;
189
+ static int nRow = 0;
189190
190191
if( p->nErr ) return 0;
191192
nByte = sizeof(GraphRow);
192193
nByte += sizeof(pRow->aParent[0])*nParent;
193194
pRow = (GraphRow*)safeMalloc( nByte );
@@ -207,11 +208,11 @@
207208
}else{
208209
p->pLast->pNext = pRow;
209210
}
210211
p->pLast = pRow;
211212
p->nRow++;
212
- pRow->idx = pRow->idxTop = p->nRow;
213
+ pRow->idx = pRow->idxTop = ++nRow;
213214
return pRow->idx;
214215
}
215216
216217
/*
217218
** Return the index of a rail currently not in use for any row between
218219
--- src/graph.c
+++ src/graph.c
@@ -184,10 +184,11 @@
184 const char *zUuid, /* hash name of the object being graphed */
185 int isLeaf /* True if this row is a leaf */
186 ){
187 GraphRow *pRow;
188 int nByte;
 
189
190 if( p->nErr ) return 0;
191 nByte = sizeof(GraphRow);
192 nByte += sizeof(pRow->aParent[0])*nParent;
193 pRow = (GraphRow*)safeMalloc( nByte );
@@ -207,11 +208,11 @@
207 }else{
208 p->pLast->pNext = pRow;
209 }
210 p->pLast = pRow;
211 p->nRow++;
212 pRow->idx = pRow->idxTop = p->nRow;
213 return pRow->idx;
214 }
215
216 /*
217 ** Return the index of a rail currently not in use for any row between
218
--- src/graph.c
+++ src/graph.c
@@ -184,10 +184,11 @@
184 const char *zUuid, /* hash name of the object being graphed */
185 int isLeaf /* True if this row is a leaf */
186 ){
187 GraphRow *pRow;
188 int nByte;
189 static int nRow = 0;
190
191 if( p->nErr ) return 0;
192 nByte = sizeof(GraphRow);
193 nByte += sizeof(pRow->aParent[0])*nParent;
194 pRow = (GraphRow*)safeMalloc( nByte );
@@ -207,11 +208,11 @@
208 }else{
209 p->pLast->pNext = pRow;
210 }
211 p->pLast = pRow;
212 p->nRow++;
213 pRow->idx = pRow->idxTop = ++nRow;
214 return pRow->idx;
215 }
216
217 /*
218 ** Return the index of a rail currently not in use for any row between
219
+39 -3
--- src/info.c
+++ src/info.c
@@ -334,12 +334,15 @@
334334
db_finalize(&q);
335335
}
336336
337337
/*
338338
** Show a graph all wiki, tickets, and check-ins that refer to object zUuid.
339
+**
340
+** If zLabel is not NULL and the graph is not empty, then output zLabel as
341
+** a prefix to the graph.
339342
*/
340
-void render_backlink_graph(const char *zUuid){
343
+void render_backlink_graph(const char *zUuid, const char *zLabel){
341344
Blob sql;
342345
Stmt q;
343346
char *zGlob;
344347
zGlob = mprintf("%.5s*", zUuid);
345348
db_multi_exec(
@@ -350,17 +353,50 @@
350353
" WHERE target GLOB %Q"
351354
" AND %Q GLOB (target || '*');",
352355
zGlob, zUuid
353356
);
354357
if( !db_exists("SELECT 1 FROM ok") ) return;
355
- @ <div class="section">Referenced By</div>
358
+ if( zLabel ) cgi_printf("%s", zLabel);
359
+ blob_zero(&sql);
360
+ blob_append(&sql, timeline_query_for_www(), -1);
361
+ blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
362
+ db_prepare(&q, "%s", blob_sql_text(&sql));
363
+ www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH, 0, 0, 0, 0);
364
+ db_finalize(&q);
365
+}
366
+
367
+/*
368
+** WEBPAGE: test-backlinks
369
+**
370
+** Show a timeline of all check-ins and other events that have entries
371
+** in the backlink table. This is used for testing the rendering
372
+** of the "References" section of the /info page.
373
+*/
374
+void backlink_timeline_page(void){
375
+ Blob sql;
376
+ Stmt q;
377
+
378
+ login_check_credentials();
379
+ if( !g.perm.Read || !g.perm.RdTkt || !g.perm.RdWiki ){
380
+ login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
381
+ return;
382
+ }
383
+ style_header("Backlink Timeline (Internal Testing Use)");
384
+ db_multi_exec(
385
+ "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
386
+ "DELETE FROM ok;"
387
+ "INSERT OR IGNORE INTO ok"
388
+ " SELECT blob.rid FROM backlink, blob"
389
+ " WHERE blob.uuid BETWEEN backlink.target AND (backlink.target||'x')"
390
+ );
356391
blob_zero(&sql);
357392
blob_append(&sql, timeline_query_for_www(), -1);
358393
blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
359394
db_prepare(&q, "%s", blob_sql_text(&sql));
360395
www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH, 0, 0, 0, 0);
361396
db_finalize(&q);
397
+ style_footer();
362398
}
363399
364400
365401
/*
366402
** Append the difference between artifacts to the output
@@ -735,14 +771,14 @@
735771
}else{
736772
style_header("Check-in Information");
737773
login_anonymous_available();
738774
}
739775
db_finalize(&q1);
776
+ render_backlink_graph(zUuid, "<div class=\"section\">References</div>\n");
740777
showTags(rid);
741778
@ <div class="section">Context</div>
742779
render_checkin_context(rid, 0);
743
- render_backlink_graph(zUuid);
744780
@ <div class="section">Changes</div>
745781
@ <div class="sectionmenu">
746782
verboseFlag = g.zPath[0]!='c';
747783
if( db_get_boolean("show-version-diffs", 0)==0 ){
748784
verboseFlag = !verboseFlag;
749785
--- src/info.c
+++ src/info.c
@@ -334,12 +334,15 @@
334 db_finalize(&q);
335 }
336
337 /*
338 ** Show a graph all wiki, tickets, and check-ins that refer to object zUuid.
 
 
 
339 */
340 void render_backlink_graph(const char *zUuid){
341 Blob sql;
342 Stmt q;
343 char *zGlob;
344 zGlob = mprintf("%.5s*", zUuid);
345 db_multi_exec(
@@ -350,17 +353,50 @@
350 " WHERE target GLOB %Q"
351 " AND %Q GLOB (target || '*');",
352 zGlob, zUuid
353 );
354 if( !db_exists("SELECT 1 FROM ok") ) return;
355 @ <div class="section">Referenced By</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
356 blob_zero(&sql);
357 blob_append(&sql, timeline_query_for_www(), -1);
358 blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
359 db_prepare(&q, "%s", blob_sql_text(&sql));
360 www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH, 0, 0, 0, 0);
361 db_finalize(&q);
 
362 }
363
364
365 /*
366 ** Append the difference between artifacts to the output
@@ -735,14 +771,14 @@
735 }else{
736 style_header("Check-in Information");
737 login_anonymous_available();
738 }
739 db_finalize(&q1);
 
740 showTags(rid);
741 @ <div class="section">Context</div>
742 render_checkin_context(rid, 0);
743 render_backlink_graph(zUuid);
744 @ <div class="section">Changes</div>
745 @ <div class="sectionmenu">
746 verboseFlag = g.zPath[0]!='c';
747 if( db_get_boolean("show-version-diffs", 0)==0 ){
748 verboseFlag = !verboseFlag;
749
--- src/info.c
+++ src/info.c
@@ -334,12 +334,15 @@
334 db_finalize(&q);
335 }
336
337 /*
338 ** Show a graph all wiki, tickets, and check-ins that refer to object zUuid.
339 **
340 ** If zLabel is not NULL and the graph is not empty, then output zLabel as
341 ** a prefix to the graph.
342 */
343 void render_backlink_graph(const char *zUuid, const char *zLabel){
344 Blob sql;
345 Stmt q;
346 char *zGlob;
347 zGlob = mprintf("%.5s*", zUuid);
348 db_multi_exec(
@@ -350,17 +353,50 @@
353 " WHERE target GLOB %Q"
354 " AND %Q GLOB (target || '*');",
355 zGlob, zUuid
356 );
357 if( !db_exists("SELECT 1 FROM ok") ) return;
358 if( zLabel ) cgi_printf("%s", zLabel);
359 blob_zero(&sql);
360 blob_append(&sql, timeline_query_for_www(), -1);
361 blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
362 db_prepare(&q, "%s", blob_sql_text(&sql));
363 www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH, 0, 0, 0, 0);
364 db_finalize(&q);
365 }
366
367 /*
368 ** WEBPAGE: test-backlinks
369 **
370 ** Show a timeline of all check-ins and other events that have entries
371 ** in the backlink table. This is used for testing the rendering
372 ** of the "References" section of the /info page.
373 */
374 void backlink_timeline_page(void){
375 Blob sql;
376 Stmt q;
377
378 login_check_credentials();
379 if( !g.perm.Read || !g.perm.RdTkt || !g.perm.RdWiki ){
380 login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
381 return;
382 }
383 style_header("Backlink Timeline (Internal Testing Use)");
384 db_multi_exec(
385 "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
386 "DELETE FROM ok;"
387 "INSERT OR IGNORE INTO ok"
388 " SELECT blob.rid FROM backlink, blob"
389 " WHERE blob.uuid BETWEEN backlink.target AND (backlink.target||'x')"
390 );
391 blob_zero(&sql);
392 blob_append(&sql, timeline_query_for_www(), -1);
393 blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
394 db_prepare(&q, "%s", blob_sql_text(&sql));
395 www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH, 0, 0, 0, 0);
396 db_finalize(&q);
397 style_footer();
398 }
399
400
401 /*
402 ** Append the difference between artifacts to the output
@@ -735,14 +771,14 @@
771 }else{
772 style_header("Check-in Information");
773 login_anonymous_available();
774 }
775 db_finalize(&q1);
776 render_backlink_graph(zUuid, "<div class=\"section\">References</div>\n");
777 showTags(rid);
778 @ <div class="section">Context</div>
779 render_checkin_context(rid, 0);
 
780 @ <div class="section">Changes</div>
781 @ <div class="sectionmenu">
782 verboseFlag = g.zPath[0]!='c';
783 if( db_get_boolean("show-version-diffs", 0)==0 ){
784 verboseFlag = !verboseFlag;
785
+19 -8
--- src/timeline.c
+++ src/timeline.c
@@ -201,10 +201,18 @@
201201
}
202202
@ <input type="submit">
203203
@ </form>
204204
style_footer();
205205
}
206
+
207
+/*
208
+** Return a new timelineTable id.
209
+*/
210
+int timeline_tableid(void){
211
+ static int id = 0;
212
+ return id++;
213
+}
206214
207215
/*
208216
** Output a timeline in the web format given a query. The query
209217
** should return these columns:
210218
**
@@ -241,10 +249,11 @@
241249
int pendingEndTr = 0; /* True if a </td></tr> is needed */
242250
int vid = 0; /* Current checkout version */
243251
int dateFormat = 0; /* 0: HH:MM (default) */
244252
int bCommentGitStyle = 0; /* Only show comments through first blank line */
245253
const char *zDateFmt;
254
+ int iTableId = timeline_tableid();
246255
247256
if( fossil_strcmp(g.zIpAddr, "127.0.0.1")==0 && db_open_local(0) ){
248257
vid = db_lget_int("checkout", 0);
249258
}
250259
zPrevDate[0] = 0;
@@ -259,11 +268,11 @@
259268
db_static_prepare(&qbranch,
260269
"SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
261270
TAG_BRANCH
262271
);
263272
264
- @ <table id="timelineTable" class="timelineTable">
273
+ @ <table id="timelineTable%d(iTableId)" class="timelineTable">
265274
blob_zero(&comment);
266275
while( db_step(pQuery)==SQLITE_ROW ){
267276
int rid = db_column_int(pQuery, 0);
268277
const char *zUuid = db_column_text(pQuery, 1);
269278
int isLeaf = db_column_int(pQuery, 5);
@@ -623,11 +632,11 @@
623632
@ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
624633
}
625634
}
626635
@ </table>
627636
if( fchngQueryInit ) db_finalize(&fchngQuery);
628
- timeline_output_graph_javascript(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0, 0);
637
+ timeline_output_graph_javascript(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0, iTableId, 0);
629638
}
630639
631640
/*
632641
** Change the RGB background color given in the argument in a foreground
633642
** color with the same hue.
@@ -664,10 +673,11 @@
664673
** graph.
665674
*/
666675
void timeline_output_graph_javascript(
667676
GraphContext *pGraph, /* The graph to be displayed */
668677
int omitDescenders, /* True to omit descenders */
678
+ int iTableId, /* Identifier for the timelineTable */
669679
int fileDiff /* True for file diff. False for check-in diff */
670680
){
671681
if( pGraph && pGraph->nErr==0 && pGraph->nRow>0 ){
672682
GraphRow *pRow;
673683
int i;
@@ -699,11 +709,11 @@
699709
/* the rowinfo[] array contains all the information needed to generate
700710
** the graph. Each entry contains information for a single row:
701711
**
702712
** id: The id of the <div> element for the row. This is an integer.
703713
** to get an actual id, prepend "m" to the integer. The top node
704
- ** is 1 and numbers increase moving down the timeline.
714
+ ** is topRow and numbers increase moving down the timeline.
705715
** bg: The background color for this row
706716
** r: The "rail" that the node for this row sits on. The left-most
707717
** rail is 0 and the number increases to the right.
708718
** d: True if there is a "descender" - an arrow coming from the bottom
709719
** of the page straight up to this node.
@@ -725,10 +735,11 @@
725735
** negative, then the rail position is the absolute value of mi[]
726736
** and a thin merge-arrow descender is drawn to the bottom of
727737
** the screen.
728738
** h: The artifact hash of the object being graphed
729739
*/
740
+ if( pGraph->pFirst ) cgi_printf("var topRow = %d\n", pGraph->pFirst->idx);
730741
cgi_printf("var rowinfo = [\n");
731742
for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
732743
cgi_printf("{id:%d,bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,f:%d,au:",
733744
pRow->idx, /* id */
734745
pRow->zBgClr, /* bg */
@@ -772,11 +783,11 @@
772783
@ var canvasDiv;
773784
@ var railPitch;
774785
@ var mergeOffset;
775786
@ var node, arrow, arrowSmall, line, mArrow, mLine, wArrow, wLine;
776787
@ function initGraph(){
777
- @ var parent = gebi("timelineTable").rows[0].cells[1];
788
+ @ var parent = gebi("timelineTable%d(iTableId)").rows[0].cells[1];
778789
@ parent.style.verticalAlign = "top";
779790
@ canvasDiv = document.createElement("div");
780791
@ canvasDiv.className = "tl-canvas";
781792
@ canvasDiv.style.position = "absolute";
782793
@ parent.appendChild(canvasDiv);
@@ -901,11 +912,11 @@
901912
@ var x = p.x + (p.r<rail ? node.w : -mArrow.w);
902913
@ var cls = "arrow merge " + (p.r<rail ? "l" : "r");
903914
@ drawBox(cls,null,x,y+(mLine.w-mArrow.h)/2);
904915
@ }
905916
@ function drawNode(p, btm){
906
- @ if( p.u>0 ) drawUpArrow(p,rowinfo[p.u-1],p.fg);
917
+ @ if( p.u>0 ) drawUpArrow(p,rowinfo[p.u-topRow],p.fg);
907918
@ var cls = node.cls;
908919
@ if( p.mi.length ) cls += " merge";
909920
@ if( p.f&1 ) cls += " leaf";
910921
@ var n = drawBox(cls,p.bg,p.x,p.y);
911922
@ n.id = "tln"+p.id;
@@ -916,11 +927,11 @@
916927
@ if( p.d ) drawUpArrow({x: p.x, y: btm-node.h/2},p,p.fg);
917928
}
918929
@ if( p.mo>=0 ){
919930
@ var x0 = p.x + node.w/2;
920931
@ var x1 = p.mo*railPitch + node.w/2;
921
- @ var u = rowinfo[p.mu-1];
932
+ @ var u = rowinfo[p.mu-topRow];
922933
@ var y1 = miLineY(u);
923934
@ if( p.u<0 || p.mo!=p.r ){
924935
@ x1 += mergeLines[p.mo] = -mLine.w/2;
925936
@ var y0 = p.y+2;
926937
@ if( p.r!=p.mo ) drawMergeLine(x0,y0,x1+(x0<x1 ? mLine.w : 0),null);
@@ -940,11 +951,11 @@
940951
@ if( x0<x1 ){
941952
@ x0 = Math.ceil(x0);
942953
@ x1 += line.w;
943954
@ }
944955
@ var y0 = p.y + (node.h-line.w)/2;
945
- @ var u = rowinfo[p.au[i+1]-1];
956
+ @ var u = rowinfo[p.au[i+1]-topRow];
946957
@ if( u.id<p.id ){
947958
@ drawLine(line,u.fg,x0,y0,x1,null);
948959
@ drawUpArrow(p,u,u.fg);
949960
@ }else{
950961
@ var y1 = u.y + (node.h-line.w)/2;
@@ -986,11 +997,11 @@
986997
@ drawNode(rowinfo[i], btm);
987998
@ }
988999
@ }
9891000
@ var selRow;
9901001
@ function clickOnNode(){
991
- @ var p = rowinfo[parseInt(this.id.match(/\d+$/)[0], 10)-1];
1002
+ @ var p = rowinfo[parseInt(this.id.match(/\d+$/)[0], 10)-topRow];
9921003
@ if( !selRow ){
9931004
@ selRow = p;
9941005
@ this.className += " sel";
9951006
@ canvasDiv.className += " sel";
9961007
@ }else if( selRow==p ){
9971008
--- src/timeline.c
+++ src/timeline.c
@@ -201,10 +201,18 @@
201 }
202 @ <input type="submit">
203 @ </form>
204 style_footer();
205 }
 
 
 
 
 
 
 
 
206
207 /*
208 ** Output a timeline in the web format given a query. The query
209 ** should return these columns:
210 **
@@ -241,10 +249,11 @@
241 int pendingEndTr = 0; /* True if a </td></tr> is needed */
242 int vid = 0; /* Current checkout version */
243 int dateFormat = 0; /* 0: HH:MM (default) */
244 int bCommentGitStyle = 0; /* Only show comments through first blank line */
245 const char *zDateFmt;
 
246
247 if( fossil_strcmp(g.zIpAddr, "127.0.0.1")==0 && db_open_local(0) ){
248 vid = db_lget_int("checkout", 0);
249 }
250 zPrevDate[0] = 0;
@@ -259,11 +268,11 @@
259 db_static_prepare(&qbranch,
260 "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
261 TAG_BRANCH
262 );
263
264 @ <table id="timelineTable" class="timelineTable">
265 blob_zero(&comment);
266 while( db_step(pQuery)==SQLITE_ROW ){
267 int rid = db_column_int(pQuery, 0);
268 const char *zUuid = db_column_text(pQuery, 1);
269 int isLeaf = db_column_int(pQuery, 5);
@@ -623,11 +632,11 @@
623 @ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
624 }
625 }
626 @ </table>
627 if( fchngQueryInit ) db_finalize(&fchngQuery);
628 timeline_output_graph_javascript(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0, 0);
629 }
630
631 /*
632 ** Change the RGB background color given in the argument in a foreground
633 ** color with the same hue.
@@ -664,10 +673,11 @@
664 ** graph.
665 */
666 void timeline_output_graph_javascript(
667 GraphContext *pGraph, /* The graph to be displayed */
668 int omitDescenders, /* True to omit descenders */
 
669 int fileDiff /* True for file diff. False for check-in diff */
670 ){
671 if( pGraph && pGraph->nErr==0 && pGraph->nRow>0 ){
672 GraphRow *pRow;
673 int i;
@@ -699,11 +709,11 @@
699 /* the rowinfo[] array contains all the information needed to generate
700 ** the graph. Each entry contains information for a single row:
701 **
702 ** id: The id of the <div> element for the row. This is an integer.
703 ** to get an actual id, prepend "m" to the integer. The top node
704 ** is 1 and numbers increase moving down the timeline.
705 ** bg: The background color for this row
706 ** r: The "rail" that the node for this row sits on. The left-most
707 ** rail is 0 and the number increases to the right.
708 ** d: True if there is a "descender" - an arrow coming from the bottom
709 ** of the page straight up to this node.
@@ -725,10 +735,11 @@
725 ** negative, then the rail position is the absolute value of mi[]
726 ** and a thin merge-arrow descender is drawn to the bottom of
727 ** the screen.
728 ** h: The artifact hash of the object being graphed
729 */
 
730 cgi_printf("var rowinfo = [\n");
731 for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
732 cgi_printf("{id:%d,bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,f:%d,au:",
733 pRow->idx, /* id */
734 pRow->zBgClr, /* bg */
@@ -772,11 +783,11 @@
772 @ var canvasDiv;
773 @ var railPitch;
774 @ var mergeOffset;
775 @ var node, arrow, arrowSmall, line, mArrow, mLine, wArrow, wLine;
776 @ function initGraph(){
777 @ var parent = gebi("timelineTable").rows[0].cells[1];
778 @ parent.style.verticalAlign = "top";
779 @ canvasDiv = document.createElement("div");
780 @ canvasDiv.className = "tl-canvas";
781 @ canvasDiv.style.position = "absolute";
782 @ parent.appendChild(canvasDiv);
@@ -901,11 +912,11 @@
901 @ var x = p.x + (p.r<rail ? node.w : -mArrow.w);
902 @ var cls = "arrow merge " + (p.r<rail ? "l" : "r");
903 @ drawBox(cls,null,x,y+(mLine.w-mArrow.h)/2);
904 @ }
905 @ function drawNode(p, btm){
906 @ if( p.u>0 ) drawUpArrow(p,rowinfo[p.u-1],p.fg);
907 @ var cls = node.cls;
908 @ if( p.mi.length ) cls += " merge";
909 @ if( p.f&1 ) cls += " leaf";
910 @ var n = drawBox(cls,p.bg,p.x,p.y);
911 @ n.id = "tln"+p.id;
@@ -916,11 +927,11 @@
916 @ if( p.d ) drawUpArrow({x: p.x, y: btm-node.h/2},p,p.fg);
917 }
918 @ if( p.mo>=0 ){
919 @ var x0 = p.x + node.w/2;
920 @ var x1 = p.mo*railPitch + node.w/2;
921 @ var u = rowinfo[p.mu-1];
922 @ var y1 = miLineY(u);
923 @ if( p.u<0 || p.mo!=p.r ){
924 @ x1 += mergeLines[p.mo] = -mLine.w/2;
925 @ var y0 = p.y+2;
926 @ if( p.r!=p.mo ) drawMergeLine(x0,y0,x1+(x0<x1 ? mLine.w : 0),null);
@@ -940,11 +951,11 @@
940 @ if( x0<x1 ){
941 @ x0 = Math.ceil(x0);
942 @ x1 += line.w;
943 @ }
944 @ var y0 = p.y + (node.h-line.w)/2;
945 @ var u = rowinfo[p.au[i+1]-1];
946 @ if( u.id<p.id ){
947 @ drawLine(line,u.fg,x0,y0,x1,null);
948 @ drawUpArrow(p,u,u.fg);
949 @ }else{
950 @ var y1 = u.y + (node.h-line.w)/2;
@@ -986,11 +997,11 @@
986 @ drawNode(rowinfo[i], btm);
987 @ }
988 @ }
989 @ var selRow;
990 @ function clickOnNode(){
991 @ var p = rowinfo[parseInt(this.id.match(/\d+$/)[0], 10)-1];
992 @ if( !selRow ){
993 @ selRow = p;
994 @ this.className += " sel";
995 @ canvasDiv.className += " sel";
996 @ }else if( selRow==p ){
997
--- src/timeline.c
+++ src/timeline.c
@@ -201,10 +201,18 @@
201 }
202 @ <input type="submit">
203 @ </form>
204 style_footer();
205 }
206
207 /*
208 ** Return a new timelineTable id.
209 */
210 int timeline_tableid(void){
211 static int id = 0;
212 return id++;
213 }
214
215 /*
216 ** Output a timeline in the web format given a query. The query
217 ** should return these columns:
218 **
@@ -241,10 +249,11 @@
249 int pendingEndTr = 0; /* True if a </td></tr> is needed */
250 int vid = 0; /* Current checkout version */
251 int dateFormat = 0; /* 0: HH:MM (default) */
252 int bCommentGitStyle = 0; /* Only show comments through first blank line */
253 const char *zDateFmt;
254 int iTableId = timeline_tableid();
255
256 if( fossil_strcmp(g.zIpAddr, "127.0.0.1")==0 && db_open_local(0) ){
257 vid = db_lget_int("checkout", 0);
258 }
259 zPrevDate[0] = 0;
@@ -259,11 +268,11 @@
268 db_static_prepare(&qbranch,
269 "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
270 TAG_BRANCH
271 );
272
273 @ <table id="timelineTable%d(iTableId)" class="timelineTable">
274 blob_zero(&comment);
275 while( db_step(pQuery)==SQLITE_ROW ){
276 int rid = db_column_int(pQuery, 0);
277 const char *zUuid = db_column_text(pQuery, 1);
278 int isLeaf = db_column_int(pQuery, 5);
@@ -623,11 +632,11 @@
632 @ <tr class="timelineBottom"><td></td><td></td><td></td></tr>
633 }
634 }
635 @ </table>
636 if( fchngQueryInit ) db_finalize(&fchngQuery);
637 timeline_output_graph_javascript(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0, iTableId, 0);
638 }
639
640 /*
641 ** Change the RGB background color given in the argument in a foreground
642 ** color with the same hue.
@@ -664,10 +673,11 @@
673 ** graph.
674 */
675 void timeline_output_graph_javascript(
676 GraphContext *pGraph, /* The graph to be displayed */
677 int omitDescenders, /* True to omit descenders */
678 int iTableId, /* Identifier for the timelineTable */
679 int fileDiff /* True for file diff. False for check-in diff */
680 ){
681 if( pGraph && pGraph->nErr==0 && pGraph->nRow>0 ){
682 GraphRow *pRow;
683 int i;
@@ -699,11 +709,11 @@
709 /* the rowinfo[] array contains all the information needed to generate
710 ** the graph. Each entry contains information for a single row:
711 **
712 ** id: The id of the <div> element for the row. This is an integer.
713 ** to get an actual id, prepend "m" to the integer. The top node
714 ** is topRow and numbers increase moving down the timeline.
715 ** bg: The background color for this row
716 ** r: The "rail" that the node for this row sits on. The left-most
717 ** rail is 0 and the number increases to the right.
718 ** d: True if there is a "descender" - an arrow coming from the bottom
719 ** of the page straight up to this node.
@@ -725,10 +735,11 @@
735 ** negative, then the rail position is the absolute value of mi[]
736 ** and a thin merge-arrow descender is drawn to the bottom of
737 ** the screen.
738 ** h: The artifact hash of the object being graphed
739 */
740 if( pGraph->pFirst ) cgi_printf("var topRow = %d\n", pGraph->pFirst->idx);
741 cgi_printf("var rowinfo = [\n");
742 for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
743 cgi_printf("{id:%d,bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,f:%d,au:",
744 pRow->idx, /* id */
745 pRow->zBgClr, /* bg */
@@ -772,11 +783,11 @@
783 @ var canvasDiv;
784 @ var railPitch;
785 @ var mergeOffset;
786 @ var node, arrow, arrowSmall, line, mArrow, mLine, wArrow, wLine;
787 @ function initGraph(){
788 @ var parent = gebi("timelineTable%d(iTableId)").rows[0].cells[1];
789 @ parent.style.verticalAlign = "top";
790 @ canvasDiv = document.createElement("div");
791 @ canvasDiv.className = "tl-canvas";
792 @ canvasDiv.style.position = "absolute";
793 @ parent.appendChild(canvasDiv);
@@ -901,11 +912,11 @@
912 @ var x = p.x + (p.r<rail ? node.w : -mArrow.w);
913 @ var cls = "arrow merge " + (p.r<rail ? "l" : "r");
914 @ drawBox(cls,null,x,y+(mLine.w-mArrow.h)/2);
915 @ }
916 @ function drawNode(p, btm){
917 @ if( p.u>0 ) drawUpArrow(p,rowinfo[p.u-topRow],p.fg);
918 @ var cls = node.cls;
919 @ if( p.mi.length ) cls += " merge";
920 @ if( p.f&1 ) cls += " leaf";
921 @ var n = drawBox(cls,p.bg,p.x,p.y);
922 @ n.id = "tln"+p.id;
@@ -916,11 +927,11 @@
927 @ if( p.d ) drawUpArrow({x: p.x, y: btm-node.h/2},p,p.fg);
928 }
929 @ if( p.mo>=0 ){
930 @ var x0 = p.x + node.w/2;
931 @ var x1 = p.mo*railPitch + node.w/2;
932 @ var u = rowinfo[p.mu-topRow];
933 @ var y1 = miLineY(u);
934 @ if( p.u<0 || p.mo!=p.r ){
935 @ x1 += mergeLines[p.mo] = -mLine.w/2;
936 @ var y0 = p.y+2;
937 @ if( p.r!=p.mo ) drawMergeLine(x0,y0,x1+(x0<x1 ? mLine.w : 0),null);
@@ -940,11 +951,11 @@
951 @ if( x0<x1 ){
952 @ x0 = Math.ceil(x0);
953 @ x1 += line.w;
954 @ }
955 @ var y0 = p.y + (node.h-line.w)/2;
956 @ var u = rowinfo[p.au[i+1]-topRow];
957 @ if( u.id<p.id ){
958 @ drawLine(line,u.fg,x0,y0,x1,null);
959 @ drawUpArrow(p,u,u.fg);
960 @ }else{
961 @ var y1 = u.y + (node.h-line.w)/2;
@@ -986,11 +997,11 @@
997 @ drawNode(rowinfo[i], btm);
998 @ }
999 @ }
1000 @ var selRow;
1001 @ function clickOnNode(){
1002 @ var p = rowinfo[parseInt(this.id.match(/\d+$/)[0], 10)-topRow];
1003 @ if( !selRow ){
1004 @ selRow = p;
1005 @ this.className += " sel";
1006 @ canvasDiv.className += " sel";
1007 @ }else if( selRow==p ){
1008

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button