| | @@ -810,33 +810,49 @@ |
| 810 | 810 | style_submenu_element(zMenuName, zMenuName, "%s", |
| 811 | 811 | url_render(pUrl, zParam, zValue, zRemove, 0)); |
| 812 | 812 | } |
| 813 | 813 | |
| 814 | 814 | |
| 815 | +/* |
| 816 | +** Convert a symbolic name used as an argument to the a=, b=, or c= |
| 817 | +** query parameters of timeline into a julianday mtime value. |
| 818 | +*/ |
| 819 | +double symbolic_name_to_mtime(const char *z){ |
| 820 | + double mtime; |
| 821 | + int rid; |
| 822 | + if( z==0 ) return -1.0; |
| 823 | + rid = symbolic_name_to_rid(z, "ci"); |
| 824 | + if( rid==0 ) return -1.0; |
| 825 | + mtime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); |
| 826 | + return mtime; |
| 827 | +} |
| 828 | + |
| 829 | +/* |
| 830 | +** The value of one second in julianday notation |
| 831 | +*/ |
| 832 | +#define ONE_SECOND (1.0/86400.0) |
| 833 | + |
| 815 | 834 | /* |
| 816 | 835 | ** zDate is a localtime date. Insert records into the |
| 817 | 836 | ** "timeline" table to cause <hr> to be inserted before and after |
| 818 | 837 | ** entries of that date. If zDate==NULL then put dividers around |
| 819 | 838 | ** the event identified by rid. |
| 820 | 839 | */ |
| 821 | | -static void timeline_add_dividers(const char *zDate, int rid){ |
| 840 | +static void timeline_add_dividers(double rDate, int rid){ |
| 822 | 841 | char *zToDel = 0; |
| 823 | | - if( zDate==0 ){ |
| 824 | | - zToDel = db_text(0,"SELECT julianday(mtime,'localtime') FROM event" |
| 825 | | - " WHERE objid=%d", rid); |
| 826 | | - zDate = zToDel; |
| 827 | | - if( zDate==0 ) zDate = "1"; |
| 842 | + if( rDate==0 ){ |
| 843 | + rDate = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); |
| 828 | 844 | } |
| 829 | 845 | db_multi_exec( |
| 830 | 846 | "INSERT INTO timeline(rid,sortby,etype)" |
| 831 | | - "VALUES(-1,julianday(%Q,'utc')-1.0e-5,'div')", |
| 832 | | - zDate |
| 847 | + "VALUES(-1,%.16g,'div')", |
| 848 | + rDate-ONE_SECOND |
| 833 | 849 | ); |
| 834 | 850 | db_multi_exec( |
| 835 | 851 | "INSERT INTO timeline(rid,sortby,etype)" |
| 836 | | - "VALUES(-2,julianday(%Q,'utc')+1.0e-5,'div')", |
| 837 | | - zDate |
| 852 | + "VALUES(-2,%.17g,'div')", |
| 853 | + rDate+ONE_SECOND |
| 838 | 854 | ); |
| 839 | 855 | fossil_free(zToDel); |
| 840 | 856 | } |
| 841 | 857 | |
| 842 | 858 | |
| | @@ -843,14 +859,14 @@ |
| 843 | 859 | /* |
| 844 | 860 | ** WEBPAGE: timeline |
| 845 | 861 | ** |
| 846 | 862 | ** Query parameters: |
| 847 | 863 | ** |
| 848 | | -** a=TIMESTAMP after this date |
| 849 | | -** b=TIMESTAMP before this date. |
| 850 | | -** c=TIMESTAMP "circa" this date. |
| 851 | | -** n=COUNT number of events in output |
| 864 | +** a=TIMEORTAG after this event |
| 865 | +** b=TIMEORTAG before this event |
| 866 | +** c=TIMEORTAG "circa" this event |
| 867 | +** n=COUNT max number of events in output |
| 852 | 868 | ** p=UUID artifact and up to COUNT parents and ancestors |
| 853 | 869 | ** d=UUID artifact and up to COUNT descendants |
| 854 | 870 | ** dp=UUUID The same as d=UUID&p=UUID |
| 855 | 871 | ** t=TAGID show only check-ins with the given tagid |
| 856 | 872 | ** r=TAGID show check-ins related to tagid |
| | @@ -901,10 +917,11 @@ |
| 901 | 917 | int to_rid = name_to_typed_rid(P("to"),"ci"); /* to= for path timelines */ |
| 902 | 918 | int noMerge = P("nomerge")!=0; /* Do not follow merge links */ |
| 903 | 919 | int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */ |
| 904 | 920 | int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */ |
| 905 | 921 | int pd_rid; |
| 922 | + double rBefore, rAfter, rCirca; /* Boundary times */ |
| 906 | 923 | |
| 907 | 924 | /* To view the timeline, must have permission to read project data. |
| 908 | 925 | */ |
| 909 | 926 | pd_rid = name_to_typed_rid(P("dp"),"ci"); |
| 910 | 927 | if( pd_rid ){ |
| | @@ -1121,63 +1138,55 @@ |
| 1121 | 1138 | blob_appendf(&sql, |
| 1122 | 1139 | " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')", |
| 1123 | 1140 | zSearch, zSearch); |
| 1124 | 1141 | url_add_parameter(&url, "s", zSearch); |
| 1125 | 1142 | } |
| 1126 | | - if( zAfter ){ |
| 1127 | | - while( fossil_isspace(zAfter[0]) ){ zAfter++; } |
| 1128 | | - if( zAfter[0] ){ |
| 1129 | | - blob_appendf(&sql, |
| 1130 | | - " AND event.mtime>=(SELECT julianday(%Q, 'utc'))" |
| 1131 | | - " ORDER BY event.mtime ASC", zAfter); |
| 1132 | | - url_add_parameter(&url, "a", zAfter); |
| 1133 | | - zBefore = 0; |
| 1134 | | - }else{ |
| 1135 | | - zAfter = 0; |
| 1136 | | - } |
| 1137 | | - }else if( zBefore ){ |
| 1138 | | - while( fossil_isspace(zBefore[0]) ){ zBefore++; } |
| 1139 | | - if( zBefore[0] ){ |
| 1140 | | - blob_appendf(&sql, |
| 1141 | | - " AND event.mtime<=(SELECT julianday(%Q, 'utc'))" |
| 1142 | | - " ORDER BY event.mtime DESC", zBefore); |
| 1143 | | - url_add_parameter(&url, "b", zBefore); |
| 1144 | | - }else{ |
| 1145 | | - zBefore = 0; |
| 1146 | | - } |
| 1147 | | - }else if( zCirca ){ |
| 1148 | | - while( fossil_isspace(zCirca[0]) ){ zCirca++; } |
| 1149 | | - if( zCirca[0] ){ |
| 1150 | | - double rCirca = db_double(0.0, "SELECT julianday(%Q, 'utc')", zCirca); |
| 1151 | | - Blob sql2; |
| 1152 | | - blob_init(&sql2, blob_str(&sql), -1); |
| 1153 | | - blob_appendf(&sql2, |
| 1154 | | - " AND event.mtime<=%f ORDER BY event.mtime DESC LIMIT %d", |
| 1155 | | - rCirca, (nEntry+1)/2 |
| 1156 | | - ); |
| 1157 | | - db_multi_exec("%s", blob_str(&sql2)); |
| 1158 | | - blob_reset(&sql2); |
| 1159 | | - blob_appendf(&sql, |
| 1160 | | - " AND event.mtime>=%f ORDER BY event.mtime ASC", |
| 1161 | | - rCirca |
| 1162 | | - ); |
| 1163 | | - nEntry -= (nEntry+1)/2; |
| 1164 | | - if( useDividers ) timeline_add_dividers(zCirca, 0); |
| 1165 | | - url_add_parameter(&url, "c", zCirca); |
| 1166 | | - }else{ |
| 1167 | | - zCirca = 0; |
| 1168 | | - } |
| 1143 | + rBefore = symbolic_name_to_mtime(zBefore); |
| 1144 | + rAfter = symbolic_name_to_mtime(zAfter); |
| 1145 | + rCirca = symbolic_name_to_mtime(zCirca); |
| 1146 | + if( rAfter>0.0 ){ |
| 1147 | + if( rBefore>0.0 ){ |
| 1148 | + blob_appendf(&sql, |
| 1149 | + " AND event.mtime>=%.17g AND event.mtime<=%.17g" |
| 1150 | + " ORDER BY event.mtime ASC", rAfter-ONE_SECOND, rBefore+ONE_SECOND); |
| 1151 | + url_add_parameter(&url, "a", zAfter); |
| 1152 | + url_add_parameter(&url, "b", zBefore); |
| 1153 | + nEntry = 1000000; |
| 1154 | + }else{ |
| 1155 | + blob_appendf(&sql, |
| 1156 | + " AND event.mtime>=%.17g ORDER BY event.mtime ASC", |
| 1157 | + rAfter-ONE_SECOND); |
| 1158 | + url_add_parameter(&url, "a", zAfter); |
| 1159 | + } |
| 1160 | + }else if( rBefore>0.0 ){ |
| 1161 | + blob_appendf(&sql, |
| 1162 | + " AND event.mtime<=%.17g ORDER BY event.mtime DESC", |
| 1163 | + rBefore+ONE_SECOND); |
| 1164 | + url_add_parameter(&url, "b", zBefore); |
| 1165 | + }else if( rCirca>0.0 ){ |
| 1166 | + Blob sql2; |
| 1167 | + blob_init(&sql2, blob_str(&sql), -1); |
| 1168 | + blob_appendf(&sql2, |
| 1169 | + " AND event.mtime<=%f ORDER BY event.mtime DESC LIMIT %d", |
| 1170 | + rCirca, (nEntry+1)/2 |
| 1171 | + ); |
| 1172 | + db_multi_exec("%s", blob_str(&sql2)); |
| 1173 | + blob_reset(&sql2); |
| 1174 | + blob_appendf(&sql, |
| 1175 | + " AND event.mtime>=%f ORDER BY event.mtime ASC", |
| 1176 | + rCirca |
| 1177 | + ); |
| 1178 | + nEntry -= (nEntry+1)/2; |
| 1179 | + if( useDividers ) timeline_add_dividers(rCirca, 0); |
| 1180 | + url_add_parameter(&url, "c", zCirca); |
| 1169 | 1181 | }else{ |
| 1170 | 1182 | blob_appendf(&sql, " ORDER BY event.mtime DESC"); |
| 1171 | 1183 | } |
| 1172 | 1184 | blob_appendf(&sql, " LIMIT %d", nEntry); |
| 1173 | 1185 | db_multi_exec("%s", blob_str(&sql)); |
| 1174 | 1186 | |
| 1175 | 1187 | n = db_int(0, "SELECT count(*) FROM timeline /*scan*/"); |
| 1176 | | - if( n<nEntry && zAfter ){ |
| 1177 | | - cgi_redirect(url_render(&url, "a", 0, "b", 0)); |
| 1178 | | - } |
| 1179 | 1188 | if( zAfter==0 && zBefore==0 && zCirca==0 ){ |
| 1180 | 1189 | blob_appendf(&desc, "%d most recent %ss", n, zEType); |
| 1181 | 1190 | }else{ |
| 1182 | 1191 | blob_appendf(&desc, "%d %ss", n, zEType); |
| 1183 | 1192 | } |
| | @@ -1190,15 +1199,20 @@ |
| 1190 | 1199 | tmFlags |= TIMELINE_DISJOINT; |
| 1191 | 1200 | }else if( zBrName ){ |
| 1192 | 1201 | blob_appendf(&desc, " related to \"%h\"", zBrName); |
| 1193 | 1202 | tmFlags |= TIMELINE_DISJOINT; |
| 1194 | 1203 | } |
| 1195 | | - if( zAfter ){ |
| 1196 | | - blob_appendf(&desc, " occurring on or after %h.<br />", zAfter); |
| 1197 | | - }else if( zBefore ){ |
| 1204 | + if( rAfter>0.0 ){ |
| 1205 | + if( rBefore>0.0 ){ |
| 1206 | + blob_appendf(&desc, " occurring between %h and %h.<br>", |
| 1207 | + zAfter, zBefore); |
| 1208 | + }else{ |
| 1209 | + blob_appendf(&desc, " occurring on or after %h.<br />", zAfter); |
| 1210 | + } |
| 1211 | + }else if( rBefore>0.0 ){ |
| 1198 | 1212 | blob_appendf(&desc, " occurring on or before %h.<br />", zBefore); |
| 1199 | | - }else if( zCirca ){ |
| 1213 | + }else if( rCirca>0.0 ){ |
| 1200 | 1214 | blob_appendf(&desc, " occurring around %h.<br />", zCirca); |
| 1201 | 1215 | } |
| 1202 | 1216 | if( zSearch ){ |
| 1203 | 1217 | blob_appendf(&desc, " matching \"%h\"", zSearch); |
| 1204 | 1218 | } |
| 1205 | 1219 | |