Diff
Not logged in

Differences From:

File src/timeline.c part of check-in [4d03017923] - Put a colored asterisk beside entries of interest in the timeline, rather than doing lots of annoying text color and background changes. by drh on 2007-08-30 21:37:42. [view]

To:

File src/timeline.c part of check-in [9a76119307] - Fix a C++-ism in the timeline module. by drh on 2007-09-10 00:48:55. Also file src/timeline.c part of check-in [bbcb6326c9] - Pulled in the navbar and timeline changes. by aku on 2007-09-17 00:58:51. [view]

@@ -23,8 +23,9 @@
 **
 ** This file contains code to implement the timeline web page
 **
 */
+#include <string.h>
 #include "config.h"
 #include "timeline.h"
 
 /*
@@ -88,14 +89,20 @@
 **    7.  True if is a leaf
 */
 void www_print_timeline(
   Stmt *pQuery,
-  char *zLastDate,
+  int *pFirstEvent,
+  int *pLastEvent,
   int (*xCallback)(int, Blob*),
   Blob *pArg
  ){
   char zPrevDate[20];
+  int cnt = 0;
   zPrevDate[0] = 0;
+  db_multi_exec(
+     "CREATE TEMP TABLE IF NOT EXISTS seen(rid INTEGER PRIMARY KEY);"
+     "DELETE FROM seen;"
+  );
   @ <table cellspacing=0 border=0 cellpadding=0>
   while( db_step(pQuery)==SQLITE_ROW ){
     int rid = db_column_int(pQuery, 0);
     const char *zUuid = db_column_text(pQuery, 1);
@@ -102,8 +109,15 @@
     int nPChild = db_column_int(pQuery, 5);
     int nParent = db_column_int(pQuery, 6);
     int isLeaf = db_column_int(pQuery, 7);
     const char *zDate = db_column_text(pQuery, 2);
+    if( cnt==0 && pFirstEvent ){
+      *pFirstEvent = rid;
+    }
+    if( pLastEvent ){
+      *pLastEvent = rid;
+    }
+    db_multi_exec("INSERT OR IGNORE INTO seen VALUES(%d)", rid);
     if( xCallback ){
       xCallback(rid, pArg);
     }
     if( memcmp(zDate, zPrevDate, 10) ){
@@ -133,11 +147,8 @@
       @ <b>Leaf</b>
     }
     @ %h(db_column_text(pQuery,3))
     @ (by %h(db_column_text(pQuery,4)))</td></tr>
-    if( zLastDate ){
-      strcpy(zLastDate, zDate);
-    }
   }
   @ </table>
 }
 
@@ -173,16 +184,31 @@
 }
 
 /*
 ** WEBPAGE: timeline
+**
+** Query parameters:
+**
+**    d=STARTDATE    date in iso8601 notation.          dflt: newest event
+**    n=INTEGER      number of events to show.          dflt: 25
+**    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
 */
 void page_timeline(void){
   Stmt q;
   char *zSQL;
   Blob scriptInit;
   char zDate[100];
   const char *zStart = P("d");
-  int nEntry = atoi(PD("n","25"));
+  int nEntry = atoi(PD("n","20"));
+  const char *zUser = P("u");
+  int objid = atoi(PD("e","0"));
+  int relatedEvents = P("r")!=0;
+  int afterFlag = P("a")!=0;
+  int firstEvent;
+  int lastEvent;
 
   /* To view the timeline, must have permission to read project data.
   */
   login_check_credentials();
@@ -203,21 +229,44 @@
     "       NOT EXISTS (SELECT 1 FROM plink WHERE pid=blob.rid)"
     "  FROM event, blob"
     " WHERE event.type='ci' AND blob.rid=event.objid"
   );
+  if( zUser ){
+    zSQL = mprintf("%z AND event.user=%Q", zSQL, zUser);
+  }
+  if( objid ){
+    char *z = db_text(0, "SELECT datetime(event.mtime) FROM event"
+                         " WHERE objid=%d", objid);
+    if( z ){
+      zStart = z;
+    }
+  }
   if( zStart ){
     while( isspace(zStart[0]) ){ zStart++; }
     if( zStart[0] ){
-      zSQL = mprintf("%z AND event.mtime<=julianday(%Q, 'localtime')",
-                      zSQL, zStart);
+      zSQL = mprintf("%z AND event.mtime %s julianday(%Q, 'localtime')",
+                      zSQL, afterFlag ? ">=" : "<=", zStart);
+    }
+  }
+  if( relatedEvents && objid ){
+    db_multi_exec(
+       "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
+    );
+    if( afterFlag ){
+      compute_descendents(objid, nEntry);
+    }else{
+      compute_ancestors(objid, nEntry);
     }
+    zSQL = mprintf("%z AND event.objid IN ok", zSQL);
   }
   zSQL = mprintf("%z ORDER BY event.mtime DESC LIMIT %d", zSQL, nEntry);
   db_prepare(&q, zSQL);
   free(zSQL);
   zDate[0] = 0;
   blob_zero(&scriptInit);
-  www_print_timeline(&q, zDate, save_parentage_javascript, &scriptInit);
+  zDate[0] = 0;
+  www_print_timeline(&q, &firstEvent, &lastEvent,
+                     save_parentage_javascript, &scriptInit);
   db_finalize(&q);
   if( zStart==0 ){
     zStart = zDate;
   }
@@ -351,47 +400,106 @@
 
 /*
 ** COMMAND: timeline
 **
-** Usage: %fossil timeline ?DATETIME? ?-n|--count N?
+** Usage: %fossil timeline ?WHEN? ?UUID|DATETIME? ?-n|--count N?
 **
 ** Print a summary of activity going backwards in date and time
 ** specified or from the current date and time if no arguments
-** are given.  Show as many as N (default 20) check-ins.
+** are given.  Show as many as N (default 20) check-ins.  The
+** WHEN argument can be any unique abbreviation of one of these
+** keywords:
+**
+**     before
+**     after
+**     descendents | children
+**     ancestors | parents
 **
-** The date and time should be in the ISO8601 format.  For
-** examples: "2007-08-18 07:21:21".  The time may be omitted.
-** Times are according to the local timezone.
+** The UUID can be any unique prefix of 4 characters or more.
+** The DATETIME should be in the ISO8601 format.  For
+** examples: "2007-08-18 07:21:21".  You can also say "current"
+** for the current version or "now" for the current time.
 */
 void timeline_cmd(void){
   Stmt q;
-  int n;
+  int n, k;
   const char *zCount;
+  char *zOrigin;
   char *zDate;
+  char *zSQL;
+  int objid = 0;
+  Blob uuid;
+  int mode = 1 ;       /* 1: before  2:after  3:children  4:parents */
   db_find_and_open_repository();
   zCount = find_option("n","count",1);
   if( zCount ){
     n = atoi(zCount);
   }else{
     n = 20;
   }
-  if( g.argc!=2 && g.argc!=3 ){
-    usage("YYYY-MM-DDtHH:MM:SS");
+  if( g.argc==4 ){
+    k = strlen(g.argv[2]);
+    if( strncmp(g.argv[2],"before",k)==0 ){
+      mode = 1;
+    }else if( strncmp(g.argv[2],"after",k)==0 && k>1 ){
+      mode = 2;
+    }else if( strncmp(g.argv[2],"descendents",k)==0 ){
+      mode = 3;
+    }else if( strncmp(g.argv[2],"children",k)==0 ){
+      mode = 3;
+    }else if( strncmp(g.argv[2],"ancestors",k)==0 && k>1 ){
+      mode = 4;
+    }else if( strncmp(g.argv[2],"parents",k)==0 ){
+      mode = 4;
+    }else{
+      usage("?WHEN? ?UUID|DATETIME?");
+    }
+    zOrigin = g.argv[3];
+  }else if( g.argc==3 ){
+    zOrigin = g.argv[2];
+  }else{
+    zOrigin = "now";
   }
-  if( g.argc==3 ){
-    zDate = g.argv[2];
+  k = strlen(zOrigin);
+  blob_zero(&uuid);
+  blob_append(&uuid, zOrigin, -1);
+  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'))");
+  }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 ){
+    objid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &uuid);
+    zDate = mprintf("(SELECT mtime FROM plink WHERE cid=%d)", objid);
   }else{
-    zDate = "now";
+    if( mode==3 || mode==4 ){
+      fossil_fatal("cannot compute descendents or ancestors of a date");
+    }
+    zDate = mprintf("(SELECT julianday(%Q, 'utc'))", zOrigin);
   }
-  db_prepare(&q,
+  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<=(SELECT julianday(%Q,'utc'))"
-    " ORDER BY event.mtime DESC", zDate
+    "   AND event.mtime %s %s",
+    (mode==1 || mode==4) ? "<=" : ">=", zDate
   );
+  if( mode==3 || mode==4 ){
+    db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
+    if( mode==3 ){
+      compute_descendents(objid, n);
+    }else{
+      compute_ancestors(objid, n);
+    }
+    zSQL = mprintf("%z AND blob.rid IN ok", zSQL);
+  }
+  zSQL = mprintf("%z ORDER BY event.mtime DESC", zSQL);
+  db_prepare(&q, zSQL);
   print_timeline(&q, n);
   db_finalize(&q);
 }