Fossil SCM

New setting timeline-mark-leaves controls whether or not leaf check-ins are identified on the timeline, and if they are identifier whether or not the markings are emphasized.

drh 2025-10-19 18:51 timeline-enhance-2025
Commit 57caee0317900b866aac9c6677555b14a38988bd19cd800d9f397834022004d4
2 files changed +11 +30 -65
+11
--- src/setup.c
+++ src/setup.c
@@ -988,10 +988,15 @@
988988
"1", "HH:MM:SS",
989989
"2", "YYYY-MM-DD HH:MM",
990990
"3", "YYMMDD HH:MM",
991991
"4", "(off)"
992992
};
993
+ static const char *const azLeafMark[] = {
994
+ "0", "No",
995
+ "1", "Yes",
996
+ "2", "Yes - with emphasis",
997
+ };
993998
login_check_credentials();
994999
if( !g.perm.Admin ){
9951000
login_needed(0);
9961001
return;
9971002
}
@@ -1076,10 +1081,16 @@
10761081
@ in a separate box (using CSS class "timelineDate") whenever the date
10771082
@ changes. With the "YYYY-MM-DD HH:MM" and "YYMMDD ..." formats,
10781083
@ the complete date and time is shown on every timeline entry using the
10791084
@ CSS class "timelineTime". (Property: "timeline-date-format")</p>
10801085
1086
+ @ <hr>
1087
+ multiple_choice_attribute("Leaf Markings", "timeline-mark-leaves",
1088
+ "tml", "1", count(azLeafMark)/2, azLeafMark);
1089
+ @ <p>Should timeline entries for leaf check-ins be identified in the
1090
+ @ detail section. (Property: "timeline-mark-leaves")</p>
1091
+
10811092
@ <hr>
10821093
entry_attribute("Max timeline comment length", 6,
10831094
"timeline-max-comment", "tmc", "0", 0);
10841095
@ <p>The maximum length of a comment to be displayed in a timeline.
10851096
@ "0" there is no length limit.
10861097
--- src/setup.c
+++ src/setup.c
@@ -988,10 +988,15 @@
988 "1", "HH:MM:SS",
989 "2", "YYYY-MM-DD HH:MM",
990 "3", "YYMMDD HH:MM",
991 "4", "(off)"
992 };
 
 
 
 
 
993 login_check_credentials();
994 if( !g.perm.Admin ){
995 login_needed(0);
996 return;
997 }
@@ -1076,10 +1081,16 @@
1076 @ in a separate box (using CSS class "timelineDate") whenever the date
1077 @ changes. With the "YYYY-MM-DD&nbsp;HH:MM" and "YYMMDD ..." formats,
1078 @ the complete date and time is shown on every timeline entry using the
1079 @ CSS class "timelineTime". (Property: "timeline-date-format")</p>
1080
 
 
 
 
 
 
1081 @ <hr>
1082 entry_attribute("Max timeline comment length", 6,
1083 "timeline-max-comment", "tmc", "0", 0);
1084 @ <p>The maximum length of a comment to be displayed in a timeline.
1085 @ "0" there is no length limit.
1086
--- src/setup.c
+++ src/setup.c
@@ -988,10 +988,15 @@
988 "1", "HH:MM:SS",
989 "2", "YYYY-MM-DD HH:MM",
990 "3", "YYMMDD HH:MM",
991 "4", "(off)"
992 };
993 static const char *const azLeafMark[] = {
994 "0", "No",
995 "1", "Yes",
996 "2", "Yes - with emphasis",
997 };
998 login_check_credentials();
999 if( !g.perm.Admin ){
1000 login_needed(0);
1001 return;
1002 }
@@ -1076,10 +1081,16 @@
1081 @ in a separate box (using CSS class "timelineDate") whenever the date
1082 @ changes. With the "YYYY-MM-DD&nbsp;HH:MM" and "YYMMDD ..." formats,
1083 @ the complete date and time is shown on every timeline entry using the
1084 @ CSS class "timelineTime". (Property: "timeline-date-format")</p>
1085
1086 @ <hr>
1087 multiple_choice_attribute("Leaf Markings", "timeline-mark-leaves",
1088 "tml", "1", count(azLeafMark)/2, azLeafMark);
1089 @ <p>Should timeline entries for leaf check-ins be identified in the
1090 @ detail section. (Property: "timeline-mark-leaves")</p>
1091
1092 @ <hr>
1093 entry_attribute("Max timeline comment length", 6,
1094 "timeline-max-comment", "tmc", "0", 0);
1095 @ <p>The maximum length of a comment to be displayed in a timeline.
1096 @ "0" there is no length limit.
1097
+30 -65
--- src/timeline.c
+++ src/timeline.c
@@ -200,61 +200,31 @@
200200
201201
if( (tmFlags & TIMELINE_INLINE)!=0 ){
202202
cgi_printf("(");
203203
}
204204
205
- /* The EXTRA_FORMAT macro is an integer that controls various experiments
206
- ** in the layout of the extra text.
207
- **
208
- ** 0 Legacy appearance.
209
- ** 1 Deemphasize "Leaf" and "Closed-Leaf". Highlight check-in hash.
210
- ** 2 Omit "Leaf"/"Closed-Leaf". But check-in hash at the end.
211
- */
212
-#define EXTRA_FORMAT 1
213
-#if EXTRA_FORMAT==0
214
- if( (tmFlags & TIMELINE_CLASSIC)==0 ){
215
- if( zType[0]=='c' ){
216
- int isLeaf = db_column_int(pQuery, 5);
217
- if( isLeaf ){
218
- if( has_closed_tag(rid) ){
219
- @ <span class='timelineLeaf'>Closed-Leaf</span>
220
- }else{
221
- @ <span class='timelineLeaf'>Leaf</span>
222
- }
223
- }
224
- cgi_printf("check-in:&nbsp;%z%S</a> ",
225
- href("%R/info/%!S",zUuid),zUuid);
226
- }else if( zType[0]=='e' && tagid ){
227
- cgi_printf("technote:&nbsp;");
228
- hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
229
- }else{
230
- cgi_printf("artifact:&nbsp;%z%S</a> ",
231
- href("%R/info/%!S",zUuid),zUuid);
232
- }
233
- }else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t'
234
- || zType[0]=='n' || zType[0]=='f'){
235
- cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
236
- }
237
-#endif /* EXTRA_FORMAT==0 */
238
-#if EXTRA_FORMAT==1
239
- if( (tmFlags & TIMELINE_CLASSIC)==0 ){
240
- if( zType[0]=='c' ){
241
- const char *zPrefix;
205
+ if( (tmFlags & TIMELINE_CLASSIC)==0 ){
206
+ if( zType[0]=='c' ){
207
+ const char *zPrefix = 0;
208
+ static int markLeaves = -1;
209
+ if( markLeaves<0 ){
210
+ markLeaves = db_get_int("timeline-mark-leaves",1);
211
+ if( markLeaves<0 ) markLeaves = 1;
212
+ }
242213
if( strcmp(zUuid, MANIFEST_UUID)==0 ){
243214
/* This will only ever happen when Fossil is drawing a timeline for
244215
** its own self-host repository. If the timeline shows the specific
245216
** check-in corresponding to the current executable, then tag that
246217
** check-in with "自" (Simplified Chinese for "self"). */
247218
zPrefix = "自&nbsp;";
248
- }else if( tmFlags & TIMELINE_SIMPLE ){
249
- zPrefix = "";
250
- }else{
251
- int isLeaf = db_column_int(pQuery, 5);
252
- if( isLeaf ){
219
+ }else if( markLeaves && db_column_int(pQuery,5) ){
220
+ if( markLeaves==1 ){
253221
zPrefix = has_closed_tag(rid) ? "closed&nbsp;" : "leaf&nbsp;";
254222
}else{
255
- zPrefix = "";
223
+ zPrefix = has_closed_tag(rid) ?
224
+ "<span class='timelineLeaf'>Closed-Leaf</span>\n" :
225
+ "<span class='timelineLeaf'>Leaf</span>\n";
256226
}
257227
}
258228
cgi_printf("%scheck-in:&nbsp;%z<span class='timelineHash'>"
259229
"%S</span></a> ",
260230
zPrefix, href("%R/info/%!S",zUuid),zUuid);
@@ -267,11 +237,10 @@
267237
}
268238
}else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t'
269239
|| zType[0]=='n' || zType[0]=='f'){
270240
cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
271241
}
272
-#endif /* EXTRA_FORMAT==1 */
273242
274243
if( (tmFlags & TIMELINE_SIMPLE)!=0 ){
275244
@ <span class='timelineEllipsis' id='ellipsis-%d(rid)' \
276245
@ data-id='%d(rid)'>...</span>
277246
@ <span class='clutter' id='detail-%d(rid)'>
@@ -332,30 +301,10 @@
332301
tag_private_status(rid);
333302
334303
if( (tmFlags & TIMELINE_SIMPLE)!=0 ){
335304
cgi_printf("</span>"); /* End of the declutter span */
336305
}
337
-
338
-#if EXTRA_FORMAT==2
339
- if( (tmFlags & TIMELINE_CLASSIC)==0 ){
340
- if( zType[0]=='c' ){
341
- cgi_printf(" check-in:&nbsp;%z<span class='timelineHash'>"
342
- "%S</span></a>",
343
- href("%R/info/%!S",zUuid),zUuid);
344
- }else if( zType[0]=='e' && tagid ){
345
- cgi_printf(" technote:&nbsp;");
346
- hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
347
- }else{
348
- cgi_printf(" artifact:&nbsp;%z%S</a>",
349
- href("%R/info/%!S",zUuid),zUuid);
350
- }
351
- }else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t'
352
- || zType[0]=='n' || zType[0]=='f'){
353
- cgi_printf(" artifact:&nbsp;%z%S</a>",href("%R/info/%!S",zUuid),zUuid);
354
- }
355
-#endif /* EXTRA_FORMAT==2 */
356
-
357306
358307
/* End timelineDetail */
359308
if( (tmFlags & TIMELINE_INLINE)!=0 ){
360309
cgi_printf(")");
361310
}
@@ -377,11 +326,27 @@
377326
** on the far left-hand side of the screen, normally targets another
378327
** /timeline page that shows the entry in context. However, if this
379328
** option is turned on, that hyperlink targets the /info page showing
380329
** the details of the entry.
381330
*/
382
-
331
+/*
332
+** SETTING: timeline-mark-leaves width=5 default=1
333
+**
334
+** Determine whether or not leaf check-ins are marked as such in the
335
+** details section of the timeline. The value is an integer between 0
336
+** and 2:
337
+**
338
+** 0 Do not show any special marking for leaf check-ins.
339
+**
340
+** 1 Show just "leaf" or "closed"
341
+**
342
+** 2 Show "Leaf" or "Closed-Leaf" with emphasis
343
+**
344
+** The default is currently 1. Prior to 2025-10-19, the default was 2.
345
+** This setting has no effect on the "Classic" view, which always behaves
346
+** as if the setting were 2.
347
+*/
383348
384349
/*
385350
** Output a timeline in the web format given a query. The query
386351
** should return these columns:
387352
**
388353
--- src/timeline.c
+++ src/timeline.c
@@ -200,61 +200,31 @@
200
201 if( (tmFlags & TIMELINE_INLINE)!=0 ){
202 cgi_printf("(");
203 }
204
205 /* The EXTRA_FORMAT macro is an integer that controls various experiments
206 ** in the layout of the extra text.
207 **
208 ** 0 Legacy appearance.
209 ** 1 Deemphasize "Leaf" and "Closed-Leaf". Highlight check-in hash.
210 ** 2 Omit "Leaf"/"Closed-Leaf". But check-in hash at the end.
211 */
212 #define EXTRA_FORMAT 1
213 #if EXTRA_FORMAT==0
214 if( (tmFlags & TIMELINE_CLASSIC)==0 ){
215 if( zType[0]=='c' ){
216 int isLeaf = db_column_int(pQuery, 5);
217 if( isLeaf ){
218 if( has_closed_tag(rid) ){
219 @ <span class='timelineLeaf'>Closed-Leaf</span>
220 }else{
221 @ <span class='timelineLeaf'>Leaf</span>
222 }
223 }
224 cgi_printf("check-in:&nbsp;%z%S</a> ",
225 href("%R/info/%!S",zUuid),zUuid);
226 }else if( zType[0]=='e' && tagid ){
227 cgi_printf("technote:&nbsp;");
228 hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
229 }else{
230 cgi_printf("artifact:&nbsp;%z%S</a> ",
231 href("%R/info/%!S",zUuid),zUuid);
232 }
233 }else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t'
234 || zType[0]=='n' || zType[0]=='f'){
235 cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
236 }
237 #endif /* EXTRA_FORMAT==0 */
238 #if EXTRA_FORMAT==1
239 if( (tmFlags & TIMELINE_CLASSIC)==0 ){
240 if( zType[0]=='c' ){
241 const char *zPrefix;
242 if( strcmp(zUuid, MANIFEST_UUID)==0 ){
243 /* This will only ever happen when Fossil is drawing a timeline for
244 ** its own self-host repository. If the timeline shows the specific
245 ** check-in corresponding to the current executable, then tag that
246 ** check-in with "自" (Simplified Chinese for "self"). */
247 zPrefix = "自&nbsp;";
248 }else if( tmFlags & TIMELINE_SIMPLE ){
249 zPrefix = "";
250 }else{
251 int isLeaf = db_column_int(pQuery, 5);
252 if( isLeaf ){
253 zPrefix = has_closed_tag(rid) ? "closed&nbsp;" : "leaf&nbsp;";
254 }else{
255 zPrefix = "";
 
 
256 }
257 }
258 cgi_printf("%scheck-in:&nbsp;%z<span class='timelineHash'>"
259 "%S</span></a> ",
260 zPrefix, href("%R/info/%!S",zUuid),zUuid);
@@ -267,11 +237,10 @@
267 }
268 }else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t'
269 || zType[0]=='n' || zType[0]=='f'){
270 cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
271 }
272 #endif /* EXTRA_FORMAT==1 */
273
274 if( (tmFlags & TIMELINE_SIMPLE)!=0 ){
275 @ <span class='timelineEllipsis' id='ellipsis-%d(rid)' \
276 @ data-id='%d(rid)'>...</span>
277 @ <span class='clutter' id='detail-%d(rid)'>
@@ -332,30 +301,10 @@
332 tag_private_status(rid);
333
334 if( (tmFlags & TIMELINE_SIMPLE)!=0 ){
335 cgi_printf("</span>"); /* End of the declutter span */
336 }
337
338 #if EXTRA_FORMAT==2
339 if( (tmFlags & TIMELINE_CLASSIC)==0 ){
340 if( zType[0]=='c' ){
341 cgi_printf(" check-in:&nbsp;%z<span class='timelineHash'>"
342 "%S</span></a>",
343 href("%R/info/%!S",zUuid),zUuid);
344 }else if( zType[0]=='e' && tagid ){
345 cgi_printf(" technote:&nbsp;");
346 hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
347 }else{
348 cgi_printf(" artifact:&nbsp;%z%S</a>",
349 href("%R/info/%!S",zUuid),zUuid);
350 }
351 }else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t'
352 || zType[0]=='n' || zType[0]=='f'){
353 cgi_printf(" artifact:&nbsp;%z%S</a>",href("%R/info/%!S",zUuid),zUuid);
354 }
355 #endif /* EXTRA_FORMAT==2 */
356
357
358 /* End timelineDetail */
359 if( (tmFlags & TIMELINE_INLINE)!=0 ){
360 cgi_printf(")");
361 }
@@ -377,11 +326,27 @@
377 ** on the far left-hand side of the screen, normally targets another
378 ** /timeline page that shows the entry in context. However, if this
379 ** option is turned on, that hyperlink targets the /info page showing
380 ** the details of the entry.
381 */
382
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
384 /*
385 ** Output a timeline in the web format given a query. The query
386 ** should return these columns:
387 **
388
--- src/timeline.c
+++ src/timeline.c
@@ -200,61 +200,31 @@
200
201 if( (tmFlags & TIMELINE_INLINE)!=0 ){
202 cgi_printf("(");
203 }
204
205 if( (tmFlags & TIMELINE_CLASSIC)==0 ){
206 if( zType[0]=='c' ){
207 const char *zPrefix = 0;
208 static int markLeaves = -1;
209 if( markLeaves<0 ){
210 markLeaves = db_get_int("timeline-mark-leaves",1);
211 if( markLeaves<0 ) markLeaves = 1;
212 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213 if( strcmp(zUuid, MANIFEST_UUID)==0 ){
214 /* This will only ever happen when Fossil is drawing a timeline for
215 ** its own self-host repository. If the timeline shows the specific
216 ** check-in corresponding to the current executable, then tag that
217 ** check-in with "自" (Simplified Chinese for "self"). */
218 zPrefix = "自&nbsp;";
219 }else if( markLeaves && db_column_int(pQuery,5) ){
220 if( markLeaves==1 ){
 
 
 
221 zPrefix = has_closed_tag(rid) ? "closed&nbsp;" : "leaf&nbsp;";
222 }else{
223 zPrefix = has_closed_tag(rid) ?
224 "<span class='timelineLeaf'>Closed-Leaf</span>\n" :
225 "<span class='timelineLeaf'>Leaf</span>\n";
226 }
227 }
228 cgi_printf("%scheck-in:&nbsp;%z<span class='timelineHash'>"
229 "%S</span></a> ",
230 zPrefix, href("%R/info/%!S",zUuid),zUuid);
@@ -267,11 +237,10 @@
237 }
238 }else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t'
239 || zType[0]=='n' || zType[0]=='f'){
240 cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
241 }
 
242
243 if( (tmFlags & TIMELINE_SIMPLE)!=0 ){
244 @ <span class='timelineEllipsis' id='ellipsis-%d(rid)' \
245 @ data-id='%d(rid)'>...</span>
246 @ <span class='clutter' id='detail-%d(rid)'>
@@ -332,30 +301,10 @@
301 tag_private_status(rid);
302
303 if( (tmFlags & TIMELINE_SIMPLE)!=0 ){
304 cgi_printf("</span>"); /* End of the declutter span */
305 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
307 /* End timelineDetail */
308 if( (tmFlags & TIMELINE_INLINE)!=0 ){
309 cgi_printf(")");
310 }
@@ -377,11 +326,27 @@
326 ** on the far left-hand side of the screen, normally targets another
327 ** /timeline page that shows the entry in context. However, if this
328 ** option is turned on, that hyperlink targets the /info page showing
329 ** the details of the entry.
330 */
331 /*
332 ** SETTING: timeline-mark-leaves width=5 default=1
333 **
334 ** Determine whether or not leaf check-ins are marked as such in the
335 ** details section of the timeline. The value is an integer between 0
336 ** and 2:
337 **
338 ** 0 Do not show any special marking for leaf check-ins.
339 **
340 ** 1 Show just "leaf" or "closed"
341 **
342 ** 2 Show "Leaf" or "Closed-Leaf" with emphasis
343 **
344 ** The default is currently 1. Prior to 2025-10-19, the default was 2.
345 ** This setting has no effect on the "Classic" view, which always behaves
346 ** as if the setting were 2.
347 */
348
349 /*
350 ** Output a timeline in the web format given a query. The query
351 ** should return these columns:
352 **
353

Keyboard Shortcuts

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