Diff
Not logged in

Differences From:

File src/timeline.c part of check-in [fcabd4774c] - Improvements to the CLI timeline display. When an update fails due to multiple descendents, show all of the descendents in timeline format. by drh on 2007-09-13 03:52:04. Also file src/timeline.c part of check-in [f76192b245] - Pulled the latest CLI, website, and sqlite changes into the importer branch. by aku on 2007-09-17 01:00:32. [view]

To:

File src/timeline.c part of check-in [ebb2765954] - Add the timeline display preferences page with the ability to turn on and off block markup in timeline comments and to limit the length of timeline comments. by drh on 2007-12-04 02:47:49. Also file src/timeline.c part of check-in [d0305b305a] - Merged mainline into my branch to get the newest application. by aku on 2007-12-05 08:07:46. [view]

@@ -34,9 +34,9 @@
 void hyperlink_to_uuid(const char *zUuid){
   char zShortUuid[UUID_SIZE+1];
   sprintf(zShortUuid, "%.10s", zUuid);
   if( g.okHistory ){
-    @ <a href="%s(g.zBaseURL)/vinfo/%s(zUuid)">[%s(zShortUuid)]</a>
+    @ <a href="%s(g.zBaseURL)/info/%s(zUuid)">[%s(zShortUuid)]</a>
   }else{
     @ <b>[%s(zShortUuid)]</b>
   }
 }
@@ -76,9 +76,9 @@
 }
 
 /*
 ** Output a timeline in the web format given a query.  The query
-** should return 4 columns:
+** should return these columns:
 **
 **    0.  rid
 **    1.  UUID
 **    2.  Date/Time
@@ -86,8 +86,10 @@
 **    4.  User
 **    5.  Number of non-merge children
 **    6.  Number of parents
 **    7.  True if is a leaf
+**    8.  background color
+**    9.  type ("ci", "w")
 */
 void www_print_timeline(
   Stmt *pQuery,
   int *pFirstEvent,
@@ -94,26 +96,42 @@
   int *pLastEvent,
   int (*xCallback)(int, Blob*),
   Blob *pArg
  ){
-  char zPrevDate[20];
   int cnt = 0;
+  int wikiFlags;
+  int mxWikiLen;
+  Blob comment;
+  char zPrevDate[20];
   zPrevDate[0] = 0;
+
+  mxWikiLen = db_get_int("timeline-max-comment", 0);
+  if( db_get_boolean("timeline-block-markup", 0) ){
+    wikiFlags = WIKI_INLINE;
+  }else{
+    wikiFlags = WIKI_INLINE | WIKI_NOBLOCK;
+  }
+
   db_multi_exec(
      "CREATE TEMP TABLE IF NOT EXISTS seen(rid INTEGER PRIMARY KEY);"
      "DELETE FROM seen;"
   );
   @ <table cellspacing=0 border=0 cellpadding=0>
+  blob_zero(&comment);
   while( db_step(pQuery)==SQLITE_ROW ){
     int rid = db_column_int(pQuery, 0);
     const char *zUuid = db_column_text(pQuery, 1);
     int nPChild = db_column_int(pQuery, 5);
     int nParent = db_column_int(pQuery, 6);
     int isLeaf = db_column_int(pQuery, 7);
+    const char *zBgClr = db_column_text(pQuery, 8);
     const char *zDate = db_column_text(pQuery, 2);
+    const char *zType = db_column_text(pQuery, 9);
+    const char *zUser = db_column_text(pQuery, 4);
     if( cnt==0 && pFirstEvent ){
       *pFirstEvent = rid;
     }
+    cnt++;
     if( pLastEvent ){
       *pLastEvent = rid;
     }
     db_multi_exec("INSERT OR IGNORE INTO seen VALUES(%d)", rid);
@@ -122,33 +140,47 @@
     }
     if( memcmp(zDate, zPrevDate, 10) ){
       sprintf(zPrevDate, "%.10s", zDate);
       @ <tr><td colspan=3>
-      @ <table cellpadding=2 border=0>
-      @ <tr><td bgcolor="#a0b5f4" class="border1">
-      @ <table cellpadding=2 cellspacing=0 border=0><tr>
-      @ <td bgcolor="#d0d9f4" class="bkgnd1">%s(zPrevDate)</td>
-      @ </tr></table>
-      @ </td></tr></table>
+      @   <div class="divider">%s(zPrevDate)</div>
       @ </td></tr>
     }
     @ <tr>
     @ <td valign="top">%s(&zDate[11])</td>
     @ <td width="20" align="center" valign="top">
     @ <font id="m%d(rid)" size="+1" color="white">*</font></td>
-    @ <td valign="top" align="left">
-    hyperlink_to_uuid_with_mouseover(zUuid, "xin", "xout", rid);
-    if( nParent>1 ){
-      @ <b>Merge</b>
-    }
-    if( nPChild>1 ){
-      @ <b>Fork</b>
-    }
-    if( isLeaf ){
-      @ <b>Leaf</b>
-    }
-    @ %h(db_column_text(pQuery,3))
-    @ (by %h(db_column_text(pQuery,4)))</td></tr>
+    if( zBgClr && zBgClr[0] ){
+      @ <td valign="top" align="left" bgcolor="%h(zBgClr)">
+    }else{
+      @ <td valign="top" align="left">
+    }
+    if( zType[0]=='c' ){
+      hyperlink_to_uuid_with_mouseover(zUuid, "xin", "xout", rid);
+      if( nParent>1 ){
+        @ <b>Merge</b>
+      }
+      if( nPChild>1 ){
+        @ <b>Fork</b>
+      }
+      if( isLeaf ){
+        @ <b>Leaf</b>
+      }
+    }else{
+      hyperlink_to_uuid(zUuid);
+    }
+    db_column_blob(pQuery, 3, &comment);
+    if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){
+      Blob truncated;
+      blob_zero(&truncated);
+      blob_append(&truncated, blob_buffer(&comment), mxWikiLen);
+      blob_append(&truncated, "...", 3);
+      wiki_convert(&truncated, 0, wikiFlags);
+      blob_reset(&truncated);
+    }else{
+      wiki_convert(&comment, 0, wikiFlags);
+    }
+    blob_reset(&comment);
+    @ (by %h(zUser))</td></tr>
   }
   @ </table>
 }
 
@@ -183,8 +215,31 @@
   return 0;
 }
 
 /*
+** Return a pointer to a constant string that forms the basis
+** for a timeline query for the WWW interface.
+*/
+const char *timeline_query_for_www(void){
+  static const char zBaseSql[] =
+    @ SELECT
+    @   blob.rid,
+    @   uuid,
+    @   datetime(event.mtime,'localtime') AS timestamp,
+    @   coalesce(ecomment, comment),
+    @   coalesce(euser, user),
+    @   (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim=1),
+    @   (SELECT count(*) FROM plink WHERE cid=blob.rid),
+    @   NOT EXISTS (SELECT 1 FROM plink WHERE pid=blob.rid),
+    @   coalesce(bgcolor, brbgcolor),
+    @   event.type
+    @  FROM event JOIN blob
+    @ WHERE blob.rid=event.objid
+  ;
+  return zBaseSql;
+}
+
+/*
 ** WEBPAGE: timeline
 **
 ** Query parameters:
 **
@@ -193,11 +248,14 @@
 **    e=INTEGER      starting event id.                 dflt: nil
 **    u=NAME         show only events from user.        dflt: nil
 **    a              show events after and including.   dflt: false
 **    r              show only related events.          dflt: false
+**    y=TYPE         show only TYPE ('ci' or 'w')       dflt: nil
+**    s              show the SQL                       dflt: nil
 */
 void page_timeline(void){
   Stmt q;
+  Blob sql;
   char *zSQL;
   Blob scriptInit;
   char zDate[100];
   const char *zStart = P("d");
@@ -205,8 +263,9 @@
   const char *zUser = P("u");
   int objid = atoi(PD("e","0"));
   int relatedEvents = P("r")!=0;
   int afterFlag = P("a")!=0;
+  const char *zType = P("y");
   int firstEvent;
   int lastEvent;
 
   /* To view the timeline, must have permission to read project data.
@@ -219,23 +278,20 @@
       db_exists("SELECT 1 FROM user"
                 " WHERE login='anonymous'"
                 "   AND cap LIKE '%%h%%'") ){
     @ <p><b>Note:</b> You will be able to access <u>much</u> more
-    @ historical information if <a href="%s(g.zBaseURL)/login">login</a>.</p>
-  }
-  zSQL = mprintf(
-    "SELECT blob.rid, uuid, datetime(event.mtime,'localtime'), comment, user,"
-    "       (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim=1),"
-    "       (SELECT count(*) FROM plink WHERE cid=blob.rid),"
-    "       NOT EXISTS (SELECT 1 FROM plink WHERE pid=blob.rid)"
-    "  FROM event, blob"
-    " WHERE event.type='ci' AND blob.rid=event.objid"
-  );
+    @ historical information if <a href="%s(g.zTop)/login">login</a>.</p>
+  }
+  blob_zero(&sql);
+  blob_append(&sql, timeline_query_for_www(), -1);
+  if( zType ){
+    blob_appendf(&sql, " AND event.type=%Q", zType);
+  }
   if( zUser ){
-    zSQL = mprintf("%z AND event.user=%Q", zSQL, zUser);
+    blob_appendf(&sql, " AND event.user=%Q", zUser);
   }
   if( objid ){
-    char *z = db_text(0, "SELECT datetime(event.mtime) FROM event"
+    char *z = db_text(0, "SELECT datetime(event.mtime, 'localtime') FROM event"
                          " WHERE objid=%d", objid);
     if( z ){
       zStart = z;
     }
@@ -242,10 +298,11 @@
   }
   if( zStart ){
     while( isspace(zStart[0]) ){ zStart++; }
     if( zStart[0] ){
-      zSQL = mprintf("%z AND event.mtime %s julianday(%Q, 'localtime')",
-                      zSQL, afterFlag ? ">=" : "<=", zStart);
+      blob_appendf(&sql,
+         " AND event.mtime %s (SELECT julianday(%Q, 'utc'))",
+                          afterFlag ? ">=" : "<=", zStart);
     }
   }
   if( relatedEvents && objid ){
     db_multi_exec(
@@ -255,19 +312,36 @@
       compute_descendents(objid, nEntry);
     }else{
       compute_ancestors(objid, nEntry);
     }
-    zSQL = mprintf("%z AND event.objid IN ok", zSQL);
+    blob_append(&sql, " AND event.objid IN ok", -1);
+  }
+  if( afterFlag ){
+    blob_appendf(&sql, " ORDER BY event.mtime ASC LIMIT %d",
+                 nEntry);
+  }else{
+    blob_appendf(&sql, " ORDER BY event.mtime DESC LIMIT %d",
+                 nEntry);
   }
-  zSQL = mprintf("%z ORDER BY event.mtime DESC LIMIT %d", zSQL, nEntry);
+  zSQL = blob_str(&sql);
+  if( afterFlag ){
+    zSQL = mprintf("SELECT * FROM (%s) ORDER BY timestamp DESC", zSQL);
+  }
   db_prepare(&q, zSQL);
-  free(zSQL);
+  if( P("s")!=0 ){
+    @ <hr><p>%h(zSQL)</p><hr>
+  }
+  blob_zero(&sql);
+  if( afterFlag ){
+    free(zSQL);
+  }
   zDate[0] = 0;
   blob_zero(&scriptInit);
   zDate[0] = 0;
   www_print_timeline(&q, &firstEvent, &lastEvent,
                      save_parentage_javascript, &scriptInit);
   db_finalize(&q);
+  @ <p>firstEvent=%d(firstEvent) lastEvent=%d(lastEvent)</p>
   if( zStart==0 ){
     zStart = zDate;
   }
   @ <script>
@@ -342,13 +416,20 @@
   @ Number Of Entries:
   @ <input type="text" size="4" value="%d(nEntry)" name="n">
   @ <br><input type="submit" value="Submit">
   @ </form>
+  @ <table><tr><td>
   @ <form method="GET" action="%s(g.zBaseURL)/timeline">
-  @ <input type="hidden" value="%h(zDate)" name="d">
+  @ <input type="hidden" value="%d(lastEvent)" name="e">
   @ <input type="hidden" value="%d(nEntry)" name="n">
   @ <input type="submit" value="Next %d(nEntry) Rows">
-  @ </form>
+  @ </form></td><td>
+  @ <form method="GET" action="%s(g.zBaseURL)/timeline">
+  @ <input type="hidden" value="%d(firstEvent)" name="e">
+  @ <input type="hidden" value="%d(nEntry)" name="n">
+  @ <input type="hidden" value="1" name="a">
+  @ <input type="submit" value="Previous %d(nEntry) Rows">
+  @ </form></td></tr></table>
   style_footer();
 }
 
 /*
@@ -355,13 +436,33 @@
 ** The input query q selects various records.  Print a human-readable
 ** summary of those records.
 **
 ** Limit the number of entries printed to nLine.
+**
+** The query should return these columns:
+**
+**    0.  rid
+**    1.  uuid
+**    2.  Date/Time
+**    3.  Comment string and user
+**    4.  Number of non-merge children
+**    5.  Number of parents
 */
 void print_timeline(Stmt *q, int mxLine){
   int nLine = 0;
   char zPrevDate[20];
+  const char *zCurrentUuid=0;
+  Stmt currentQ;
+  int rid = db_lget_int("checkout", 0);
   zPrevDate[0] = 0;
+
+  db_prepare(&currentQ,
+    "SELECT uuid"
+    "  FROM blob WHERE rid=%d", rid
+  );
+  if( db_step(&currentQ)==SQLITE_ROW ){
+    zCurrentUuid = db_column_text(&currentQ, 0);
+  }
 
   while( db_step(q)==SQLITE_ROW && nLine<=mxLine ){
     const char *zId = db_column_text(q, 1);
     const char *zDate = db_column_text(q, 2);
@@ -368,8 +469,10 @@
     const char *zCom = db_column_text(q, 3);
     int nChild = db_column_int(q, 4);
     int nParent = db_column_int(q, 5);
     char *zFree = 0;
+    int n = 0;
+    char zPrefix[80];
     char zUuid[UUID_SIZE+1];
 
     sprintf(zUuid, "%.10s", zId);
     if( memcmp(zDate, zPrevDate, 10) ){
@@ -378,28 +481,46 @@
       nLine++;
     }
     if( zCom==0 ) zCom = "";
     printf("%.8s ", &zDate[11]);
-    if( nChild>1 || nParent>1 ){
-      int n = 0;
-      char zPrefix[50];
-      if( nParent>1 ){
-        sqlite3_snprintf(sizeof(zPrefix), zPrefix, "*MERGE* ");
-        n = strlen(zPrefix);
-      }
-      if( nChild>1 ){
-        sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*FORK* ");
-        n = strlen(zPrefix);
-      }
-      zFree = sqlite3_mprintf("[%.10s] %s%s", zUuid, zPrefix, zCom);
-    }else{
-      zFree = sqlite3_mprintf("[%.10s] %s", zUuid, zCom);
+    zPrefix[0] = 0;
+    if( nParent>1 ){
+      sqlite3_snprintf(sizeof(zPrefix), zPrefix, "*MERGE* ");
+      n = strlen(zPrefix);
+    }
+    if( nChild>1 ){
+      sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*FORK* ");
+      n = strlen(zPrefix);
+    }
+    if( strcmp(zCurrentUuid,zId)==0 ){
+      sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*CURRENT* ");
+      n += strlen(zPrefix);
     }
+    zFree = sqlite3_mprintf("[%.10s] %s%s", zUuid, zPrefix, zCom);
     nLine += comment_print(zFree, 9, 79);
     sqlite3_free(zFree);
   }
+  db_finalize(&currentQ);
 }
 
+/*
+** Return a pointer to a static string that forms the basis for
+** a timeline query for display on a TTY.
+*/
+const char *timeline_query_for_tty(void){
+  static const char zBaseSql[] =
+    @ SELECT
+    @   blob.rid,
+    @   uuid,
+    @   datetime(event.mtime,'localtime'),
+    @   coalesce(ecomment,comment) || ' (by ' || coalesce(euser,user,'?') ||')',
+    @   (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim),
+    @   (SELECT count(*) FROM plink WHERE cid=blob.rid)
+    @ FROM event, blob
+    @ WHERE blob.rid=event.objid
+  ;
+  return zBaseSql;
+}
 
 /*
 ** COMMAND: timeline
 **
@@ -467,9 +588,9 @@
   if( strcmp(zOrigin, "now")==0 ){
     if( mode==3 || mode==4 ){
       fossil_fatal("cannot compute descendents or ancestors of a date");
     }
-    zDate = mprintf("(SELECT julianday('now','utc'))");
+    zDate = mprintf("(SELECT datetime('now'))");
   }else if( strncmp(zOrigin, "current", k)==0 ){
     objid = db_lget_int("checkout",0);
     zDate = mprintf("(SELECT mtime FROM plink WHERE cid=%d)", objid);
   }else if( name_to_uuid(&uuid, 0)==0 ){
@@ -480,17 +601,12 @@
       fossil_fatal("cannot compute descendents or ancestors of a date");
     }
     zDate = mprintf("(SELECT julianday(%Q, 'utc'))", zOrigin);
   }
-  zSQL = mprintf(
-    "SELECT blob.rid, uuid, datetime(event.mtime,'localtime'),"
-    "       comment || ' (by ' || user || ')',"
-    "       (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim),"
-    "       (SELECT count(*) FROM plink WHERE cid=blob.rid)"
-    "  FROM event, blob"
-    " WHERE event.type='ci' AND blob.rid=event.objid"
-    "   AND event.mtime %s %s",
-    (mode==1 || mode==4) ? "<=" : ">=", zDate
+  zSQL = mprintf("%s AND event.mtime %s %s",
+     timeline_query_for_tty(),
+     (mode==1 || mode==4) ? "<=" : ">=",
+     zDate
   );
   if( mode==3 || mode==4 ){
     db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
     if( mode==3 ){