Fossil SCM
Show file sizes the the treeview. Other file browser enhancements.
Commit
73fe442a258179c427b5f521f1963c773ab86e238a230ce870a5f072034f59c2
Parent
57f1e8725425e34…
2 files changed
+98
-64
+8
-1
+98
-64
| --- src/browse.c | ||
| +++ src/browse.c | ||
| @@ -271,54 +271,42 @@ | ||
| 271 | 271 | */ |
| 272 | 272 | db_multi_exec( |
| 273 | 273 | "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);" |
| 274 | 274 | ); |
| 275 | 275 | if( zCI ){ |
| 276 | - Stmt ins; | |
| 277 | - ManifestFile *pFile; | |
| 278 | - ManifestFile *pPrev = 0; | |
| 279 | - int nPrev = 0; | |
| 280 | - int c; | |
| 281 | - | |
| 282 | - db_prepare(&ins, | |
| 283 | - "INSERT OR IGNORE INTO localfiles VALUES(pathelement(:x,0), :u)" | |
| 284 | - ); | |
| 285 | - manifest_file_rewind(pM); | |
| 286 | - while( (pFile = manifest_file_next(pM,0))!=0 ){ | |
| 287 | - if( nD>0 | |
| 288 | - && (fossil_strncmp(pFile->zName, zD, nD-1)!=0 | |
| 289 | - || pFile->zName[nD-1]!='/') | |
| 290 | - ){ | |
| 291 | - continue; | |
| 292 | - } | |
| 293 | - if( pPrev | |
| 294 | - && fossil_strncmp(&pFile->zName[nD],&pPrev->zName[nD],nPrev)==0 | |
| 295 | - && (pFile->zName[nD+nPrev]==0 || pFile->zName[nD+nPrev]=='/') | |
| 296 | - ){ | |
| 297 | - continue; | |
| 298 | - } | |
| 299 | - db_bind_text(&ins, ":x", &pFile->zName[nD]); | |
| 300 | - db_bind_text(&ins, ":u", pFile->zUuid); | |
| 301 | - db_step(&ins); | |
| 302 | - db_reset(&ins); | |
| 303 | - pPrev = pFile; | |
| 304 | - for(nPrev=0; (c=pPrev->zName[nD+nPrev]) && c!='/'; nPrev++){} | |
| 305 | - if( c=='/' ) nPrev++; | |
| 306 | - } | |
| 307 | - db_finalize(&ins); | |
| 308 | - }else if( zD ){ | |
| 309 | - db_multi_exec( | |
| 310 | - "INSERT OR IGNORE INTO localfiles" | |
| 311 | - " SELECT pathelement(name,%d), NULL FROM filename" | |
| 312 | - " WHERE name GLOB '%q/*'", | |
| 313 | - nD, zD | |
| 314 | - ); | |
| 276 | + /* Files in the specific checked given by zCI */ | |
| 277 | + if( zD ){ | |
| 278 | + db_multi_exec( | |
| 279 | + "INSERT OR IGNORE INTO localfiles" | |
| 280 | + " SELECT pathelement(filename,%d), uuid" | |
| 281 | + " FROM files_of_checkin(%Q)" | |
| 282 | + " WHERE filename GLOB '%q/*'", | |
| 283 | + nD, zCI, zD | |
| 284 | + ); | |
| 285 | + }else{ | |
| 286 | + db_multi_exec( | |
| 287 | + "INSERT OR IGNORE INTO localfiles" | |
| 288 | + " SELECT pathelement(filename,%d), uuid" | |
| 289 | + " FROM files_of_checkin(%Q)", | |
| 290 | + nD, zCI | |
| 291 | + ); | |
| 292 | + } | |
| 315 | 293 | }else{ |
| 316 | - db_multi_exec( | |
| 317 | - "INSERT OR IGNORE INTO localfiles" | |
| 318 | - " SELECT pathelement(name,0), NULL FROM filename" | |
| 319 | - ); | |
| 294 | + /* All files across all check-ins */ | |
| 295 | + if( zD ){ | |
| 296 | + db_multi_exec( | |
| 297 | + "INSERT OR IGNORE INTO localfiles" | |
| 298 | + " SELECT pathelement(name,%d), NULL FROM filename" | |
| 299 | + " WHERE name GLOB '%q/*'", | |
| 300 | + nD, zD | |
| 301 | + ); | |
| 302 | + }else{ | |
| 303 | + db_multi_exec( | |
| 304 | + "INSERT OR IGNORE INTO localfiles" | |
| 305 | + " SELECT pathelement(name,0), NULL FROM filename" | |
| 306 | + ); | |
| 307 | + } | |
| 320 | 308 | } |
| 321 | 309 | |
| 322 | 310 | /* If the re=REGEXP query parameter is present, filter out names that |
| 323 | 311 | ** do not match the pattern */ |
| 324 | 312 | if( zRegexp ){ |
| @@ -442,10 +430,12 @@ | ||
| 442 | 430 | FileTreeNode *pLastChild; /* Last child on the pChild list */ |
| 443 | 431 | char *zName; /* Name of this entry. The "tail" */ |
| 444 | 432 | char *zFullName; /* Full pathname of this entry */ |
| 445 | 433 | char *zUuid; /* Artifact hash of this file. May be NULL. */ |
| 446 | 434 | double mtime; /* Modification time for this entry */ |
| 435 | + double sortBy; /* Either mtime or size, depending on desired sort order */ | |
| 436 | + int iSize; /* Size for this entry */ | |
| 447 | 437 | unsigned nFullName; /* Length of zFullName */ |
| 448 | 438 | unsigned iLevel; /* Levels of parent directories */ |
| 449 | 439 | }; |
| 450 | 440 | |
| 451 | 441 | /* |
| @@ -471,15 +461,17 @@ | ||
| 471 | 461 | */ |
| 472 | 462 | static void tree_add_node( |
| 473 | 463 | FileTree *pTree, /* Tree into which nodes are added */ |
| 474 | 464 | const char *zPath, /* The full pathname of file to add */ |
| 475 | 465 | const char *zUuid, /* Hash of the file. Might be NULL. */ |
| 476 | - double mtime /* Modification time for this entry */ | |
| 466 | + double mtime, /* Modification time for this entry */ | |
| 467 | + int size, /* Size for this entry */ | |
| 468 | + int sortOrder /* 0: filename, 1: mtime, 2: size */ | |
| 477 | 469 | ){ |
| 478 | 470 | int i; |
| 479 | 471 | FileTreeNode *pParent; /* Parent (directory) of the next node to insert */ |
| 480 | - | |
| 472 | +//fossil_print("<pre>zPath %s zUuid %s mtime %f size %d</pre>\n",zPath,zUuid,mtime,size); | |
| 481 | 473 | /* Make pParent point to the most recent ancestor of zPath, or |
| 482 | 474 | ** NULL if there are no prior entires that are a container for zPath. |
| 483 | 475 | */ |
| 484 | 476 | pParent = pTree->pLast; |
| 485 | 477 | while( pParent!=0 && |
| @@ -525,10 +517,16 @@ | ||
| 525 | 517 | }else{ |
| 526 | 518 | if( pTree->pLastTop ) pTree->pLastTop->pSibling = pNew; |
| 527 | 519 | pTree->pLastTop = pNew; |
| 528 | 520 | } |
| 529 | 521 | pNew->mtime = mtime; |
| 522 | + pNew->iSize = size; | |
| 523 | + if( sortOrder ){ | |
| 524 | + pNew->sortBy = sortOrder==1 ? mtime : (double)size; | |
| 525 | + }else{ | |
| 526 | + pNew->sortBy = 0.0; | |
| 527 | + } | |
| 530 | 528 | while( zPath[i]=='/' ){ i++; } |
| 531 | 529 | pParent = pNew; |
| 532 | 530 | } |
| 533 | 531 | while( pParent && pParent->pParent ){ |
| 534 | 532 | if( pParent->pParent->mtime < pParent->mtime ){ |
| @@ -537,19 +535,22 @@ | ||
| 537 | 535 | pParent = pParent->pParent; |
| 538 | 536 | } |
| 539 | 537 | } |
| 540 | 538 | |
| 541 | 539 | /* Comparison function for two FileTreeNode objects. Sort first by |
| 542 | -** mtime (larger numbers first) and then by zName (smaller names first). | |
| 540 | +** sortBy (larger numbers first) and then by zName (smaller names first). | |
| 541 | +** | |
| 542 | +** The sortBy field will be the same as mtime in order to sort by time, | |
| 543 | +** or the same as iSize to sort by file size. | |
| 543 | 544 | ** |
| 544 | 545 | ** Return negative if pLeft<pRight. |
| 545 | 546 | ** Return positive if pLeft>pRight. |
| 546 | 547 | ** Return zero if pLeft==pRight. |
| 547 | 548 | */ |
| 548 | 549 | static int compareNodes(FileTreeNode *pLeft, FileTreeNode *pRight){ |
| 549 | - if( pLeft->mtime>pRight->mtime ) return -1; | |
| 550 | - if( pLeft->mtime<pRight->mtime ) return +1; | |
| 550 | + if( pLeft->sortBy>pRight->sortBy ) return -1; | |
| 551 | + if( pLeft->sortBy<pRight->sortBy ) return +1; | |
| 551 | 552 | return fossil_stricmp(pLeft->zName, pRight->zName); |
| 552 | 553 | } |
| 553 | 554 | |
| 554 | 555 | /* Merge together two sorted lists of FileTreeNode objects */ |
| 555 | 556 | static FileTreeNode *mergeNodes(FileTreeNode *pLeft, FileTreeNode *pRight){ |
| @@ -571,12 +572,12 @@ | ||
| 571 | 572 | pEnd->pSibling = pRight; |
| 572 | 573 | } |
| 573 | 574 | return base.pSibling; |
| 574 | 575 | } |
| 575 | 576 | |
| 576 | -/* Sort a list of FileTreeNode objects in mtime order. */ | |
| 577 | -static FileTreeNode *sortNodesByMtime(FileTreeNode *p){ | |
| 577 | +/* Sort a list of FileTreeNode objects in sortmtime order. */ | |
| 578 | +static FileTreeNode *sortNodes(FileTreeNode *p){ | |
| 578 | 579 | FileTreeNode *a[30]; |
| 579 | 580 | FileTreeNode *pX; |
| 580 | 581 | int i; |
| 581 | 582 | |
| 582 | 583 | memset(a, 0, sizeof(a)); |
| @@ -604,16 +605,16 @@ | ||
| 604 | 605 | ** FileTreeNode.pLastChild |
| 605 | 606 | ** FileTreeNode.pNext |
| 606 | 607 | ** |
| 607 | 608 | ** Use relinkTree to reconnect the pNext pointers. |
| 608 | 609 | */ |
| 609 | -static FileTreeNode *sortTreeByMtime(FileTreeNode *p){ | |
| 610 | +static FileTreeNode *sortTree(FileTreeNode *p){ | |
| 610 | 611 | FileTreeNode *pX; |
| 611 | 612 | for(pX=p; pX; pX=pX->pSibling){ |
| 612 | - if( pX->pChild ) pX->pChild = sortTreeByMtime(pX->pChild); | |
| 613 | + if( pX->pChild ) pX->pChild = sortTree(pX->pChild); | |
| 613 | 614 | } |
| 614 | - return sortNodesByMtime(p); | |
| 615 | + return sortNodes(p); | |
| 615 | 616 | } |
| 616 | 617 | |
| 617 | 618 | /* Reconstruct the FileTree by reconnecting the FileTreeNode.pNext |
| 618 | 619 | ** fields in sequential order. |
| 619 | 620 | */ |
| @@ -648,11 +649,11 @@ | ||
| 648 | 649 | ** name=PATH Directory to display. Optional |
| 649 | 650 | ** ci=LABEL Show only files in this check-in. Optional. |
| 650 | 651 | ** re=REGEXP Show only files matching REGEXP. Optional. |
| 651 | 652 | ** expand Begin with the tree fully expanded. |
| 652 | 653 | ** nofiles Show directories (folders) only. Omit files. |
| 653 | -** mtime Order directory elements by decreasing mtime | |
| 654 | +** sort 0: by filename, 1: by mtime, 2: by size | |
| 654 | 655 | */ |
| 655 | 656 | void page_tree(void){ |
| 656 | 657 | char *zD = fossil_strdup(P("name")); |
| 657 | 658 | int nD = zD ? strlen(zD)+1 : 0; |
| 658 | 659 | const char *zCI = P("ci"); |
| @@ -661,10 +662,11 @@ | ||
| 661 | 662 | Blob dirname; |
| 662 | 663 | Manifest *pM = 0; |
| 663 | 664 | double rNow = 0; |
| 664 | 665 | char *zNow = 0; |
| 665 | 666 | int useMtime = atoi(PD("mtime","0")); |
| 667 | + int sortOrder = atoi(PD("sort",useMtime?"1":"0")); | |
| 666 | 668 | int linkTrunk = 1; /* include link to "trunk" */ |
| 667 | 669 | int linkTip = 1; /* include link to "tip" */ |
| 668 | 670 | const char *zRE; /* the value for the re=REGEXP query parameter */ |
| 669 | 671 | const char *zObjType; /* "files" by default or "folders" for "nofiles" */ |
| 670 | 672 | char *zREx = ""; /* Extra parameters for path hyperlinks */ |
| @@ -765,11 +767,18 @@ | ||
| 765 | 767 | style_submenu_element("Top-Level", "%s", |
| 766 | 768 | url_render(&sURI, "name", 0, 0, 0)); |
| 767 | 769 | }else if( zRE ){ |
| 768 | 770 | blob_appendf(&dirname, "matching \"%s\"", zRE); |
| 769 | 771 | } |
| 770 | - style_submenu_binary("mtime","Sort By Time","Sort By Filename", 0); | |
| 772 | + { | |
| 773 | + static const char *const sort_orders[] = { | |
| 774 | + "0", "Sort By Filename", | |
| 775 | + "1", "Sort By Age", | |
| 776 | + "2", "Sort By Size" | |
| 777 | + }; | |
| 778 | + style_submenu_multichoice("sort", 3, sort_orders, 0); | |
| 779 | + } | |
| 771 | 780 | if( zCI ){ |
| 772 | 781 | style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0)); |
| 773 | 782 | if( nD==0 && !showDirOnly ){ |
| 774 | 783 | style_submenu_element("File Ages", "%R/fileage?name=%T", zCI); |
| 775 | 784 | } |
| @@ -788,46 +797,49 @@ | ||
| 788 | 797 | */ |
| 789 | 798 | if( zCI ){ |
| 790 | 799 | Stmt q; |
| 791 | 800 | compute_fileage(rid, 0); |
| 792 | 801 | db_prepare(&q, |
| 793 | - "SELECT filename.name, blob.uuid, fileage.mtime\n" | |
| 802 | + "SELECT filename.name, blob.uuid, blob.size, fileage.mtime\n" | |
| 794 | 803 | " FROM fileage, filename, blob\n" |
| 795 | 804 | " WHERE filename.fnid=fileage.fnid\n" |
| 796 | 805 | " AND blob.rid=fileage.fid\n" |
| 797 | 806 | " ORDER BY filename.name COLLATE uintnocase;" |
| 798 | 807 | ); |
| 799 | 808 | while( db_step(&q)==SQLITE_ROW ){ |
| 800 | 809 | const char *zFile = db_column_text(&q,0); |
| 801 | 810 | const char *zUuid = db_column_text(&q,1); |
| 802 | - double mtime = db_column_double(&q,2); | |
| 811 | + int size = db_column_int(&q,2); | |
| 812 | + double mtime = db_column_double(&q,3); | |
| 803 | 813 | if( nD>0 && (fossil_strncmp(zFile, zD, nD-1)!=0 || zFile[nD-1]!='/') ){ |
| 804 | 814 | continue; |
| 805 | 815 | } |
| 806 | 816 | if( pRE && re_match(pRE, (const unsigned char*)zFile, -1)==0 ) continue; |
| 807 | - tree_add_node(&sTree, zFile, zUuid, mtime); | |
| 817 | + tree_add_node(&sTree, zFile, zUuid, mtime, size, sortOrder); | |
| 808 | 818 | } |
| 809 | 819 | db_finalize(&q); |
| 810 | 820 | }else{ |
| 811 | 821 | Stmt q; |
| 812 | 822 | db_prepare(&q, |
| 813 | 823 | "SELECT\n" |
| 814 | 824 | " (SELECT name FROM filename WHERE filename.fnid=mlink.fnid),\n" |
| 815 | 825 | " (SELECT uuid FROM blob WHERE blob.rid=mlink.fid),\n" |
| 826 | + " (SELECT size FROM blob WHERE blob.rid=mlink.fid),\n" | |
| 816 | 827 | " max(event.mtime)\n" |
| 817 | 828 | " FROM mlink JOIN event ON event.objid=mlink.mid\n" |
| 818 | 829 | " GROUP BY mlink.fnid\n" |
| 819 | 830 | " ORDER BY 1 COLLATE uintnocase;"); |
| 820 | 831 | while( db_step(&q)==SQLITE_ROW ){ |
| 821 | 832 | const char *zName = db_column_text(&q, 0); |
| 822 | 833 | const char *zUuid = db_column_text(&q,1); |
| 823 | - double mtime = db_column_double(&q,2); | |
| 834 | + int size = db_column_int(&q,2); | |
| 835 | + double mtime = db_column_double(&q,3); | |
| 824 | 836 | if( nD>0 && (fossil_strncmp(zName, zD, nD-1)!=0 || zName[nD-1]!='/') ){ |
| 825 | 837 | continue; |
| 826 | 838 | } |
| 827 | 839 | if( pRE && re_match(pRE, (const u8*)zName, -1)==0 ) continue; |
| 828 | - tree_add_node(&sTree, zName, zUuid, mtime); | |
| 840 | + tree_add_node(&sTree, zName, zUuid, mtime, size, sortOrder); | |
| 829 | 841 | } |
| 830 | 842 | db_finalize(&q); |
| 831 | 843 | } |
| 832 | 844 | style_submenu_checkbox("nofiles", "Folders Only", 0, 0); |
| 833 | 845 | |
| @@ -853,12 +865,14 @@ | ||
| 853 | 865 | } |
| 854 | 866 | }else{ |
| 855 | 867 | int n = db_int(0, "SELECT count(*) FROM plink"); |
| 856 | 868 | @ <h2>%s(zObjType) from all %d(n) check-ins %s(blob_str(&dirname)) |
| 857 | 869 | } |
| 858 | - if( useMtime ){ | |
| 870 | + if( sortOrder==1 ){ | |
| 859 | 871 | @ sorted by modification time</h2> |
| 872 | + }else if( sortOrder==2 ){ | |
| 873 | + @ sorted by size</h2> | |
| 860 | 874 | }else{ |
| 861 | 875 | @ sorted by filename</h2> |
| 862 | 876 | } |
| 863 | 877 | |
| 864 | 878 | if( zNow ){ |
| @@ -884,16 +898,17 @@ | ||
| 884 | 898 | @ <li class="dir subdir last"> |
| 885 | 899 | } |
| 886 | 900 | @ <div class="filetreeline"> |
| 887 | 901 | @ %z(href("%s",url_render(&sURI,"name",0,0,0)))%h(zProjectName)</a> |
| 888 | 902 | if( zNow ){ |
| 889 | - @ <div class="filetreeage">%s(zNow)</div> | |
| 903 | + @ <div class="filetreeage">Last Change</div> | |
| 904 | + @ <div class="filetreesize">Size</div> | |
| 890 | 905 | } |
| 891 | 906 | @ </div> |
| 892 | 907 | @ <ul> |
| 893 | - if( useMtime ){ | |
| 894 | - p = sortTreeByMtime(sTree.pFirst); | |
| 908 | + if( sortOrder ){ | |
| 909 | + p = sortTree(sTree.pFirst); | |
| 895 | 910 | memset(&sTree, 0, sizeof(sTree)); |
| 896 | 911 | relinkTree(&sTree, p); |
| 897 | 912 | } |
| 898 | 913 | for(p=sTree.pFirst, nDir=0; p; p=p->pNext){ |
| 899 | 914 | const char *zLastClass = p->pSibling==0 ? " last" : ""; |
| @@ -902,10 +917,11 @@ | ||
| 902 | 917 | @ <li class="dir%s(zSubdirClass)%s(zLastClass)"><div class="filetreeline"> |
| 903 | 918 | @ %z(href("%s",url_render(&sURI,"name",p->zFullName,0,0)))%h(p->zName)</a> |
| 904 | 919 | if( p->mtime>0.0 ){ |
| 905 | 920 | char *zAge = human_readable_age(rNow - p->mtime); |
| 906 | 921 | @ <div class="filetreeage">%s(zAge)</div> |
| 922 | + @ <div class="filetreesize"></div> | |
| 907 | 923 | } |
| 908 | 924 | @ </div> |
| 909 | 925 | if( startExpanded || (int)(p->nFullName)<=nD ){ |
| 910 | 926 | @ <ul id="dir%d(nDir)"> |
| 911 | 927 | }else{ |
| @@ -923,10 +939,11 @@ | ||
| 923 | 939 | @ <li class="%z(zFileClass)%s(zLastClass)"><div class="filetreeline"> |
| 924 | 940 | @ %z(zLink)%h(p->zName)</a> |
| 925 | 941 | if( p->mtime>0 ){ |
| 926 | 942 | char *zAge = human_readable_age(rNow - p->mtime); |
| 927 | 943 | @ <div class="filetreeage">%s(zAge)</div> |
| 944 | + @ <div class="filetreesize">%s(p->iSize ? mprintf("%,d",p->iSize) : "-")</div> | |
| 928 | 945 | } |
| 929 | 946 | @ </div> |
| 930 | 947 | } |
| 931 | 948 | if( p->pSibling==0 ){ |
| 932 | 949 | int nClose = p->iLevel - (p->pNext ? p->pNext->iLevel : 0); |
| @@ -1192,5 +1209,22 @@ | ||
| 1192 | 1209 | @ </table></div> |
| 1193 | 1210 | db_finalize(&q1); |
| 1194 | 1211 | db_finalize(&q2); |
| 1195 | 1212 | style_finish_page(); |
| 1196 | 1213 | } |
| 1214 | + | |
| 1215 | +/* | |
| 1216 | +** WEBPAGE: files | |
| 1217 | +** | |
| 1218 | +** Show files as a flat table. If the ci=LABEL query parameter is provided, | |
| 1219 | +** then show all the files in the specified check-in. Without the ci= query | |
| 1220 | +** parameter show all files across all check-ins. | |
| 1221 | +** | |
| 1222 | +** Query parameters: | |
| 1223 | +** | |
| 1224 | +** name=PATH Directory to display. Optional | |
| 1225 | +** ci=LABEL Show only files in this check-in. Optional. | |
| 1226 | +** re=REGEXP Show only files matching REGEXP. Optional. | |
| 1227 | +*/ | |
| 1228 | +void files_page(void){ | |
| 1229 | + return; | |
| 1230 | +} | |
| 1197 | 1231 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -271,54 +271,42 @@ | |
| 271 | */ |
| 272 | db_multi_exec( |
| 273 | "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);" |
| 274 | ); |
| 275 | if( zCI ){ |
| 276 | Stmt ins; |
| 277 | ManifestFile *pFile; |
| 278 | ManifestFile *pPrev = 0; |
| 279 | int nPrev = 0; |
| 280 | int c; |
| 281 | |
| 282 | db_prepare(&ins, |
| 283 | "INSERT OR IGNORE INTO localfiles VALUES(pathelement(:x,0), :u)" |
| 284 | ); |
| 285 | manifest_file_rewind(pM); |
| 286 | while( (pFile = manifest_file_next(pM,0))!=0 ){ |
| 287 | if( nD>0 |
| 288 | && (fossil_strncmp(pFile->zName, zD, nD-1)!=0 |
| 289 | || pFile->zName[nD-1]!='/') |
| 290 | ){ |
| 291 | continue; |
| 292 | } |
| 293 | if( pPrev |
| 294 | && fossil_strncmp(&pFile->zName[nD],&pPrev->zName[nD],nPrev)==0 |
| 295 | && (pFile->zName[nD+nPrev]==0 || pFile->zName[nD+nPrev]=='/') |
| 296 | ){ |
| 297 | continue; |
| 298 | } |
| 299 | db_bind_text(&ins, ":x", &pFile->zName[nD]); |
| 300 | db_bind_text(&ins, ":u", pFile->zUuid); |
| 301 | db_step(&ins); |
| 302 | db_reset(&ins); |
| 303 | pPrev = pFile; |
| 304 | for(nPrev=0; (c=pPrev->zName[nD+nPrev]) && c!='/'; nPrev++){} |
| 305 | if( c=='/' ) nPrev++; |
| 306 | } |
| 307 | db_finalize(&ins); |
| 308 | }else if( zD ){ |
| 309 | db_multi_exec( |
| 310 | "INSERT OR IGNORE INTO localfiles" |
| 311 | " SELECT pathelement(name,%d), NULL FROM filename" |
| 312 | " WHERE name GLOB '%q/*'", |
| 313 | nD, zD |
| 314 | ); |
| 315 | }else{ |
| 316 | db_multi_exec( |
| 317 | "INSERT OR IGNORE INTO localfiles" |
| 318 | " SELECT pathelement(name,0), NULL FROM filename" |
| 319 | ); |
| 320 | } |
| 321 | |
| 322 | /* If the re=REGEXP query parameter is present, filter out names that |
| 323 | ** do not match the pattern */ |
| 324 | if( zRegexp ){ |
| @@ -442,10 +430,12 @@ | |
| 442 | FileTreeNode *pLastChild; /* Last child on the pChild list */ |
| 443 | char *zName; /* Name of this entry. The "tail" */ |
| 444 | char *zFullName; /* Full pathname of this entry */ |
| 445 | char *zUuid; /* Artifact hash of this file. May be NULL. */ |
| 446 | double mtime; /* Modification time for this entry */ |
| 447 | unsigned nFullName; /* Length of zFullName */ |
| 448 | unsigned iLevel; /* Levels of parent directories */ |
| 449 | }; |
| 450 | |
| 451 | /* |
| @@ -471,15 +461,17 @@ | |
| 471 | */ |
| 472 | static void tree_add_node( |
| 473 | FileTree *pTree, /* Tree into which nodes are added */ |
| 474 | const char *zPath, /* The full pathname of file to add */ |
| 475 | const char *zUuid, /* Hash of the file. Might be NULL. */ |
| 476 | double mtime /* Modification time for this entry */ |
| 477 | ){ |
| 478 | int i; |
| 479 | FileTreeNode *pParent; /* Parent (directory) of the next node to insert */ |
| 480 | |
| 481 | /* Make pParent point to the most recent ancestor of zPath, or |
| 482 | ** NULL if there are no prior entires that are a container for zPath. |
| 483 | */ |
| 484 | pParent = pTree->pLast; |
| 485 | while( pParent!=0 && |
| @@ -525,10 +517,16 @@ | |
| 525 | }else{ |
| 526 | if( pTree->pLastTop ) pTree->pLastTop->pSibling = pNew; |
| 527 | pTree->pLastTop = pNew; |
| 528 | } |
| 529 | pNew->mtime = mtime; |
| 530 | while( zPath[i]=='/' ){ i++; } |
| 531 | pParent = pNew; |
| 532 | } |
| 533 | while( pParent && pParent->pParent ){ |
| 534 | if( pParent->pParent->mtime < pParent->mtime ){ |
| @@ -537,19 +535,22 @@ | |
| 537 | pParent = pParent->pParent; |
| 538 | } |
| 539 | } |
| 540 | |
| 541 | /* Comparison function for two FileTreeNode objects. Sort first by |
| 542 | ** mtime (larger numbers first) and then by zName (smaller names first). |
| 543 | ** |
| 544 | ** Return negative if pLeft<pRight. |
| 545 | ** Return positive if pLeft>pRight. |
| 546 | ** Return zero if pLeft==pRight. |
| 547 | */ |
| 548 | static int compareNodes(FileTreeNode *pLeft, FileTreeNode *pRight){ |
| 549 | if( pLeft->mtime>pRight->mtime ) return -1; |
| 550 | if( pLeft->mtime<pRight->mtime ) return +1; |
| 551 | return fossil_stricmp(pLeft->zName, pRight->zName); |
| 552 | } |
| 553 | |
| 554 | /* Merge together two sorted lists of FileTreeNode objects */ |
| 555 | static FileTreeNode *mergeNodes(FileTreeNode *pLeft, FileTreeNode *pRight){ |
| @@ -571,12 +572,12 @@ | |
| 571 | pEnd->pSibling = pRight; |
| 572 | } |
| 573 | return base.pSibling; |
| 574 | } |
| 575 | |
| 576 | /* Sort a list of FileTreeNode objects in mtime order. */ |
| 577 | static FileTreeNode *sortNodesByMtime(FileTreeNode *p){ |
| 578 | FileTreeNode *a[30]; |
| 579 | FileTreeNode *pX; |
| 580 | int i; |
| 581 | |
| 582 | memset(a, 0, sizeof(a)); |
| @@ -604,16 +605,16 @@ | |
| 604 | ** FileTreeNode.pLastChild |
| 605 | ** FileTreeNode.pNext |
| 606 | ** |
| 607 | ** Use relinkTree to reconnect the pNext pointers. |
| 608 | */ |
| 609 | static FileTreeNode *sortTreeByMtime(FileTreeNode *p){ |
| 610 | FileTreeNode *pX; |
| 611 | for(pX=p; pX; pX=pX->pSibling){ |
| 612 | if( pX->pChild ) pX->pChild = sortTreeByMtime(pX->pChild); |
| 613 | } |
| 614 | return sortNodesByMtime(p); |
| 615 | } |
| 616 | |
| 617 | /* Reconstruct the FileTree by reconnecting the FileTreeNode.pNext |
| 618 | ** fields in sequential order. |
| 619 | */ |
| @@ -648,11 +649,11 @@ | |
| 648 | ** name=PATH Directory to display. Optional |
| 649 | ** ci=LABEL Show only files in this check-in. Optional. |
| 650 | ** re=REGEXP Show only files matching REGEXP. Optional. |
| 651 | ** expand Begin with the tree fully expanded. |
| 652 | ** nofiles Show directories (folders) only. Omit files. |
| 653 | ** mtime Order directory elements by decreasing mtime |
| 654 | */ |
| 655 | void page_tree(void){ |
| 656 | char *zD = fossil_strdup(P("name")); |
| 657 | int nD = zD ? strlen(zD)+1 : 0; |
| 658 | const char *zCI = P("ci"); |
| @@ -661,10 +662,11 @@ | |
| 661 | Blob dirname; |
| 662 | Manifest *pM = 0; |
| 663 | double rNow = 0; |
| 664 | char *zNow = 0; |
| 665 | int useMtime = atoi(PD("mtime","0")); |
| 666 | int linkTrunk = 1; /* include link to "trunk" */ |
| 667 | int linkTip = 1; /* include link to "tip" */ |
| 668 | const char *zRE; /* the value for the re=REGEXP query parameter */ |
| 669 | const char *zObjType; /* "files" by default or "folders" for "nofiles" */ |
| 670 | char *zREx = ""; /* Extra parameters for path hyperlinks */ |
| @@ -765,11 +767,18 @@ | |
| 765 | style_submenu_element("Top-Level", "%s", |
| 766 | url_render(&sURI, "name", 0, 0, 0)); |
| 767 | }else if( zRE ){ |
| 768 | blob_appendf(&dirname, "matching \"%s\"", zRE); |
| 769 | } |
| 770 | style_submenu_binary("mtime","Sort By Time","Sort By Filename", 0); |
| 771 | if( zCI ){ |
| 772 | style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0)); |
| 773 | if( nD==0 && !showDirOnly ){ |
| 774 | style_submenu_element("File Ages", "%R/fileage?name=%T", zCI); |
| 775 | } |
| @@ -788,46 +797,49 @@ | |
| 788 | */ |
| 789 | if( zCI ){ |
| 790 | Stmt q; |
| 791 | compute_fileage(rid, 0); |
| 792 | db_prepare(&q, |
| 793 | "SELECT filename.name, blob.uuid, fileage.mtime\n" |
| 794 | " FROM fileage, filename, blob\n" |
| 795 | " WHERE filename.fnid=fileage.fnid\n" |
| 796 | " AND blob.rid=fileage.fid\n" |
| 797 | " ORDER BY filename.name COLLATE uintnocase;" |
| 798 | ); |
| 799 | while( db_step(&q)==SQLITE_ROW ){ |
| 800 | const char *zFile = db_column_text(&q,0); |
| 801 | const char *zUuid = db_column_text(&q,1); |
| 802 | double mtime = db_column_double(&q,2); |
| 803 | if( nD>0 && (fossil_strncmp(zFile, zD, nD-1)!=0 || zFile[nD-1]!='/') ){ |
| 804 | continue; |
| 805 | } |
| 806 | if( pRE && re_match(pRE, (const unsigned char*)zFile, -1)==0 ) continue; |
| 807 | tree_add_node(&sTree, zFile, zUuid, mtime); |
| 808 | } |
| 809 | db_finalize(&q); |
| 810 | }else{ |
| 811 | Stmt q; |
| 812 | db_prepare(&q, |
| 813 | "SELECT\n" |
| 814 | " (SELECT name FROM filename WHERE filename.fnid=mlink.fnid),\n" |
| 815 | " (SELECT uuid FROM blob WHERE blob.rid=mlink.fid),\n" |
| 816 | " max(event.mtime)\n" |
| 817 | " FROM mlink JOIN event ON event.objid=mlink.mid\n" |
| 818 | " GROUP BY mlink.fnid\n" |
| 819 | " ORDER BY 1 COLLATE uintnocase;"); |
| 820 | while( db_step(&q)==SQLITE_ROW ){ |
| 821 | const char *zName = db_column_text(&q, 0); |
| 822 | const char *zUuid = db_column_text(&q,1); |
| 823 | double mtime = db_column_double(&q,2); |
| 824 | if( nD>0 && (fossil_strncmp(zName, zD, nD-1)!=0 || zName[nD-1]!='/') ){ |
| 825 | continue; |
| 826 | } |
| 827 | if( pRE && re_match(pRE, (const u8*)zName, -1)==0 ) continue; |
| 828 | tree_add_node(&sTree, zName, zUuid, mtime); |
| 829 | } |
| 830 | db_finalize(&q); |
| 831 | } |
| 832 | style_submenu_checkbox("nofiles", "Folders Only", 0, 0); |
| 833 | |
| @@ -853,12 +865,14 @@ | |
| 853 | } |
| 854 | }else{ |
| 855 | int n = db_int(0, "SELECT count(*) FROM plink"); |
| 856 | @ <h2>%s(zObjType) from all %d(n) check-ins %s(blob_str(&dirname)) |
| 857 | } |
| 858 | if( useMtime ){ |
| 859 | @ sorted by modification time</h2> |
| 860 | }else{ |
| 861 | @ sorted by filename</h2> |
| 862 | } |
| 863 | |
| 864 | if( zNow ){ |
| @@ -884,16 +898,17 @@ | |
| 884 | @ <li class="dir subdir last"> |
| 885 | } |
| 886 | @ <div class="filetreeline"> |
| 887 | @ %z(href("%s",url_render(&sURI,"name",0,0,0)))%h(zProjectName)</a> |
| 888 | if( zNow ){ |
| 889 | @ <div class="filetreeage">%s(zNow)</div> |
| 890 | } |
| 891 | @ </div> |
| 892 | @ <ul> |
| 893 | if( useMtime ){ |
| 894 | p = sortTreeByMtime(sTree.pFirst); |
| 895 | memset(&sTree, 0, sizeof(sTree)); |
| 896 | relinkTree(&sTree, p); |
| 897 | } |
| 898 | for(p=sTree.pFirst, nDir=0; p; p=p->pNext){ |
| 899 | const char *zLastClass = p->pSibling==0 ? " last" : ""; |
| @@ -902,10 +917,11 @@ | |
| 902 | @ <li class="dir%s(zSubdirClass)%s(zLastClass)"><div class="filetreeline"> |
| 903 | @ %z(href("%s",url_render(&sURI,"name",p->zFullName,0,0)))%h(p->zName)</a> |
| 904 | if( p->mtime>0.0 ){ |
| 905 | char *zAge = human_readable_age(rNow - p->mtime); |
| 906 | @ <div class="filetreeage">%s(zAge)</div> |
| 907 | } |
| 908 | @ </div> |
| 909 | if( startExpanded || (int)(p->nFullName)<=nD ){ |
| 910 | @ <ul id="dir%d(nDir)"> |
| 911 | }else{ |
| @@ -923,10 +939,11 @@ | |
| 923 | @ <li class="%z(zFileClass)%s(zLastClass)"><div class="filetreeline"> |
| 924 | @ %z(zLink)%h(p->zName)</a> |
| 925 | if( p->mtime>0 ){ |
| 926 | char *zAge = human_readable_age(rNow - p->mtime); |
| 927 | @ <div class="filetreeage">%s(zAge)</div> |
| 928 | } |
| 929 | @ </div> |
| 930 | } |
| 931 | if( p->pSibling==0 ){ |
| 932 | int nClose = p->iLevel - (p->pNext ? p->pNext->iLevel : 0); |
| @@ -1192,5 +1209,22 @@ | |
| 1192 | @ </table></div> |
| 1193 | db_finalize(&q1); |
| 1194 | db_finalize(&q2); |
| 1195 | style_finish_page(); |
| 1196 | } |
| 1197 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -271,54 +271,42 @@ | |
| 271 | */ |
| 272 | db_multi_exec( |
| 273 | "CREATE TEMP TABLE localfiles(x UNIQUE NOT NULL, u);" |
| 274 | ); |
| 275 | if( zCI ){ |
| 276 | /* Files in the specific checked given by zCI */ |
| 277 | if( zD ){ |
| 278 | db_multi_exec( |
| 279 | "INSERT OR IGNORE INTO localfiles" |
| 280 | " SELECT pathelement(filename,%d), uuid" |
| 281 | " FROM files_of_checkin(%Q)" |
| 282 | " WHERE filename GLOB '%q/*'", |
| 283 | nD, zCI, zD |
| 284 | ); |
| 285 | }else{ |
| 286 | db_multi_exec( |
| 287 | "INSERT OR IGNORE INTO localfiles" |
| 288 | " SELECT pathelement(filename,%d), uuid" |
| 289 | " FROM files_of_checkin(%Q)", |
| 290 | nD, zCI |
| 291 | ); |
| 292 | } |
| 293 | }else{ |
| 294 | /* All files across all check-ins */ |
| 295 | if( zD ){ |
| 296 | db_multi_exec( |
| 297 | "INSERT OR IGNORE INTO localfiles" |
| 298 | " SELECT pathelement(name,%d), NULL FROM filename" |
| 299 | " WHERE name GLOB '%q/*'", |
| 300 | nD, zD |
| 301 | ); |
| 302 | }else{ |
| 303 | db_multi_exec( |
| 304 | "INSERT OR IGNORE INTO localfiles" |
| 305 | " SELECT pathelement(name,0), NULL FROM filename" |
| 306 | ); |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | /* If the re=REGEXP query parameter is present, filter out names that |
| 311 | ** do not match the pattern */ |
| 312 | if( zRegexp ){ |
| @@ -442,10 +430,12 @@ | |
| 430 | FileTreeNode *pLastChild; /* Last child on the pChild list */ |
| 431 | char *zName; /* Name of this entry. The "tail" */ |
| 432 | char *zFullName; /* Full pathname of this entry */ |
| 433 | char *zUuid; /* Artifact hash of this file. May be NULL. */ |
| 434 | double mtime; /* Modification time for this entry */ |
| 435 | double sortBy; /* Either mtime or size, depending on desired sort order */ |
| 436 | int iSize; /* Size for this entry */ |
| 437 | unsigned nFullName; /* Length of zFullName */ |
| 438 | unsigned iLevel; /* Levels of parent directories */ |
| 439 | }; |
| 440 | |
| 441 | /* |
| @@ -471,15 +461,17 @@ | |
| 461 | */ |
| 462 | static void tree_add_node( |
| 463 | FileTree *pTree, /* Tree into which nodes are added */ |
| 464 | const char *zPath, /* The full pathname of file to add */ |
| 465 | const char *zUuid, /* Hash of the file. Might be NULL. */ |
| 466 | double mtime, /* Modification time for this entry */ |
| 467 | int size, /* Size for this entry */ |
| 468 | int sortOrder /* 0: filename, 1: mtime, 2: size */ |
| 469 | ){ |
| 470 | int i; |
| 471 | FileTreeNode *pParent; /* Parent (directory) of the next node to insert */ |
| 472 | //fossil_print("<pre>zPath %s zUuid %s mtime %f size %d</pre>\n",zPath,zUuid,mtime,size); |
| 473 | /* Make pParent point to the most recent ancestor of zPath, or |
| 474 | ** NULL if there are no prior entires that are a container for zPath. |
| 475 | */ |
| 476 | pParent = pTree->pLast; |
| 477 | while( pParent!=0 && |
| @@ -525,10 +517,16 @@ | |
| 517 | }else{ |
| 518 | if( pTree->pLastTop ) pTree->pLastTop->pSibling = pNew; |
| 519 | pTree->pLastTop = pNew; |
| 520 | } |
| 521 | pNew->mtime = mtime; |
| 522 | pNew->iSize = size; |
| 523 | if( sortOrder ){ |
| 524 | pNew->sortBy = sortOrder==1 ? mtime : (double)size; |
| 525 | }else{ |
| 526 | pNew->sortBy = 0.0; |
| 527 | } |
| 528 | while( zPath[i]=='/' ){ i++; } |
| 529 | pParent = pNew; |
| 530 | } |
| 531 | while( pParent && pParent->pParent ){ |
| 532 | if( pParent->pParent->mtime < pParent->mtime ){ |
| @@ -537,19 +535,22 @@ | |
| 535 | pParent = pParent->pParent; |
| 536 | } |
| 537 | } |
| 538 | |
| 539 | /* Comparison function for two FileTreeNode objects. Sort first by |
| 540 | ** sortBy (larger numbers first) and then by zName (smaller names first). |
| 541 | ** |
| 542 | ** The sortBy field will be the same as mtime in order to sort by time, |
| 543 | ** or the same as iSize to sort by file size. |
| 544 | ** |
| 545 | ** Return negative if pLeft<pRight. |
| 546 | ** Return positive if pLeft>pRight. |
| 547 | ** Return zero if pLeft==pRight. |
| 548 | */ |
| 549 | static int compareNodes(FileTreeNode *pLeft, FileTreeNode *pRight){ |
| 550 | if( pLeft->sortBy>pRight->sortBy ) return -1; |
| 551 | if( pLeft->sortBy<pRight->sortBy ) return +1; |
| 552 | return fossil_stricmp(pLeft->zName, pRight->zName); |
| 553 | } |
| 554 | |
| 555 | /* Merge together two sorted lists of FileTreeNode objects */ |
| 556 | static FileTreeNode *mergeNodes(FileTreeNode *pLeft, FileTreeNode *pRight){ |
| @@ -571,12 +572,12 @@ | |
| 572 | pEnd->pSibling = pRight; |
| 573 | } |
| 574 | return base.pSibling; |
| 575 | } |
| 576 | |
| 577 | /* Sort a list of FileTreeNode objects in sortmtime order. */ |
| 578 | static FileTreeNode *sortNodes(FileTreeNode *p){ |
| 579 | FileTreeNode *a[30]; |
| 580 | FileTreeNode *pX; |
| 581 | int i; |
| 582 | |
| 583 | memset(a, 0, sizeof(a)); |
| @@ -604,16 +605,16 @@ | |
| 605 | ** FileTreeNode.pLastChild |
| 606 | ** FileTreeNode.pNext |
| 607 | ** |
| 608 | ** Use relinkTree to reconnect the pNext pointers. |
| 609 | */ |
| 610 | static FileTreeNode *sortTree(FileTreeNode *p){ |
| 611 | FileTreeNode *pX; |
| 612 | for(pX=p; pX; pX=pX->pSibling){ |
| 613 | if( pX->pChild ) pX->pChild = sortTree(pX->pChild); |
| 614 | } |
| 615 | return sortNodes(p); |
| 616 | } |
| 617 | |
| 618 | /* Reconstruct the FileTree by reconnecting the FileTreeNode.pNext |
| 619 | ** fields in sequential order. |
| 620 | */ |
| @@ -648,11 +649,11 @@ | |
| 649 | ** name=PATH Directory to display. Optional |
| 650 | ** ci=LABEL Show only files in this check-in. Optional. |
| 651 | ** re=REGEXP Show only files matching REGEXP. Optional. |
| 652 | ** expand Begin with the tree fully expanded. |
| 653 | ** nofiles Show directories (folders) only. Omit files. |
| 654 | ** sort 0: by filename, 1: by mtime, 2: by size |
| 655 | */ |
| 656 | void page_tree(void){ |
| 657 | char *zD = fossil_strdup(P("name")); |
| 658 | int nD = zD ? strlen(zD)+1 : 0; |
| 659 | const char *zCI = P("ci"); |
| @@ -661,10 +662,11 @@ | |
| 662 | Blob dirname; |
| 663 | Manifest *pM = 0; |
| 664 | double rNow = 0; |
| 665 | char *zNow = 0; |
| 666 | int useMtime = atoi(PD("mtime","0")); |
| 667 | int sortOrder = atoi(PD("sort",useMtime?"1":"0")); |
| 668 | int linkTrunk = 1; /* include link to "trunk" */ |
| 669 | int linkTip = 1; /* include link to "tip" */ |
| 670 | const char *zRE; /* the value for the re=REGEXP query parameter */ |
| 671 | const char *zObjType; /* "files" by default or "folders" for "nofiles" */ |
| 672 | char *zREx = ""; /* Extra parameters for path hyperlinks */ |
| @@ -765,11 +767,18 @@ | |
| 767 | style_submenu_element("Top-Level", "%s", |
| 768 | url_render(&sURI, "name", 0, 0, 0)); |
| 769 | }else if( zRE ){ |
| 770 | blob_appendf(&dirname, "matching \"%s\"", zRE); |
| 771 | } |
| 772 | { |
| 773 | static const char *const sort_orders[] = { |
| 774 | "0", "Sort By Filename", |
| 775 | "1", "Sort By Age", |
| 776 | "2", "Sort By Size" |
| 777 | }; |
| 778 | style_submenu_multichoice("sort", 3, sort_orders, 0); |
| 779 | } |
| 780 | if( zCI ){ |
| 781 | style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0)); |
| 782 | if( nD==0 && !showDirOnly ){ |
| 783 | style_submenu_element("File Ages", "%R/fileage?name=%T", zCI); |
| 784 | } |
| @@ -788,46 +797,49 @@ | |
| 797 | */ |
| 798 | if( zCI ){ |
| 799 | Stmt q; |
| 800 | compute_fileage(rid, 0); |
| 801 | db_prepare(&q, |
| 802 | "SELECT filename.name, blob.uuid, blob.size, fileage.mtime\n" |
| 803 | " FROM fileage, filename, blob\n" |
| 804 | " WHERE filename.fnid=fileage.fnid\n" |
| 805 | " AND blob.rid=fileage.fid\n" |
| 806 | " ORDER BY filename.name COLLATE uintnocase;" |
| 807 | ); |
| 808 | while( db_step(&q)==SQLITE_ROW ){ |
| 809 | const char *zFile = db_column_text(&q,0); |
| 810 | const char *zUuid = db_column_text(&q,1); |
| 811 | int size = db_column_int(&q,2); |
| 812 | double mtime = db_column_double(&q,3); |
| 813 | if( nD>0 && (fossil_strncmp(zFile, zD, nD-1)!=0 || zFile[nD-1]!='/') ){ |
| 814 | continue; |
| 815 | } |
| 816 | if( pRE && re_match(pRE, (const unsigned char*)zFile, -1)==0 ) continue; |
| 817 | tree_add_node(&sTree, zFile, zUuid, mtime, size, sortOrder); |
| 818 | } |
| 819 | db_finalize(&q); |
| 820 | }else{ |
| 821 | Stmt q; |
| 822 | db_prepare(&q, |
| 823 | "SELECT\n" |
| 824 | " (SELECT name FROM filename WHERE filename.fnid=mlink.fnid),\n" |
| 825 | " (SELECT uuid FROM blob WHERE blob.rid=mlink.fid),\n" |
| 826 | " (SELECT size FROM blob WHERE blob.rid=mlink.fid),\n" |
| 827 | " max(event.mtime)\n" |
| 828 | " FROM mlink JOIN event ON event.objid=mlink.mid\n" |
| 829 | " GROUP BY mlink.fnid\n" |
| 830 | " ORDER BY 1 COLLATE uintnocase;"); |
| 831 | while( db_step(&q)==SQLITE_ROW ){ |
| 832 | const char *zName = db_column_text(&q, 0); |
| 833 | const char *zUuid = db_column_text(&q,1); |
| 834 | int size = db_column_int(&q,2); |
| 835 | double mtime = db_column_double(&q,3); |
| 836 | if( nD>0 && (fossil_strncmp(zName, zD, nD-1)!=0 || zName[nD-1]!='/') ){ |
| 837 | continue; |
| 838 | } |
| 839 | if( pRE && re_match(pRE, (const u8*)zName, -1)==0 ) continue; |
| 840 | tree_add_node(&sTree, zName, zUuid, mtime, size, sortOrder); |
| 841 | } |
| 842 | db_finalize(&q); |
| 843 | } |
| 844 | style_submenu_checkbox("nofiles", "Folders Only", 0, 0); |
| 845 | |
| @@ -853,12 +865,14 @@ | |
| 865 | } |
| 866 | }else{ |
| 867 | int n = db_int(0, "SELECT count(*) FROM plink"); |
| 868 | @ <h2>%s(zObjType) from all %d(n) check-ins %s(blob_str(&dirname)) |
| 869 | } |
| 870 | if( sortOrder==1 ){ |
| 871 | @ sorted by modification time</h2> |
| 872 | }else if( sortOrder==2 ){ |
| 873 | @ sorted by size</h2> |
| 874 | }else{ |
| 875 | @ sorted by filename</h2> |
| 876 | } |
| 877 | |
| 878 | if( zNow ){ |
| @@ -884,16 +898,17 @@ | |
| 898 | @ <li class="dir subdir last"> |
| 899 | } |
| 900 | @ <div class="filetreeline"> |
| 901 | @ %z(href("%s",url_render(&sURI,"name",0,0,0)))%h(zProjectName)</a> |
| 902 | if( zNow ){ |
| 903 | @ <div class="filetreeage">Last Change</div> |
| 904 | @ <div class="filetreesize">Size</div> |
| 905 | } |
| 906 | @ </div> |
| 907 | @ <ul> |
| 908 | if( sortOrder ){ |
| 909 | p = sortTree(sTree.pFirst); |
| 910 | memset(&sTree, 0, sizeof(sTree)); |
| 911 | relinkTree(&sTree, p); |
| 912 | } |
| 913 | for(p=sTree.pFirst, nDir=0; p; p=p->pNext){ |
| 914 | const char *zLastClass = p->pSibling==0 ? " last" : ""; |
| @@ -902,10 +917,11 @@ | |
| 917 | @ <li class="dir%s(zSubdirClass)%s(zLastClass)"><div class="filetreeline"> |
| 918 | @ %z(href("%s",url_render(&sURI,"name",p->zFullName,0,0)))%h(p->zName)</a> |
| 919 | if( p->mtime>0.0 ){ |
| 920 | char *zAge = human_readable_age(rNow - p->mtime); |
| 921 | @ <div class="filetreeage">%s(zAge)</div> |
| 922 | @ <div class="filetreesize"></div> |
| 923 | } |
| 924 | @ </div> |
| 925 | if( startExpanded || (int)(p->nFullName)<=nD ){ |
| 926 | @ <ul id="dir%d(nDir)"> |
| 927 | }else{ |
| @@ -923,10 +939,11 @@ | |
| 939 | @ <li class="%z(zFileClass)%s(zLastClass)"><div class="filetreeline"> |
| 940 | @ %z(zLink)%h(p->zName)</a> |
| 941 | if( p->mtime>0 ){ |
| 942 | char *zAge = human_readable_age(rNow - p->mtime); |
| 943 | @ <div class="filetreeage">%s(zAge)</div> |
| 944 | @ <div class="filetreesize">%s(p->iSize ? mprintf("%,d",p->iSize) : "-")</div> |
| 945 | } |
| 946 | @ </div> |
| 947 | } |
| 948 | if( p->pSibling==0 ){ |
| 949 | int nClose = p->iLevel - (p->pNext ? p->pNext->iLevel : 0); |
| @@ -1192,5 +1209,22 @@ | |
| 1209 | @ </table></div> |
| 1210 | db_finalize(&q1); |
| 1211 | db_finalize(&q2); |
| 1212 | style_finish_page(); |
| 1213 | } |
| 1214 | |
| 1215 | /* |
| 1216 | ** WEBPAGE: files |
| 1217 | ** |
| 1218 | ** Show files as a flat table. If the ci=LABEL query parameter is provided, |
| 1219 | ** then show all the files in the specified check-in. Without the ci= query |
| 1220 | ** parameter show all files across all check-ins. |
| 1221 | ** |
| 1222 | ** Query parameters: |
| 1223 | ** |
| 1224 | ** name=PATH Directory to display. Optional |
| 1225 | ** ci=LABEL Show only files in this check-in. Optional. |
| 1226 | ** re=REGEXP Show only files matching REGEXP. Optional. |
| 1227 | */ |
| 1228 | void files_page(void){ |
| 1229 | return; |
| 1230 | } |
| 1231 |
+8
-1
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -332,12 +332,19 @@ | ||
| 332 | 332 | v\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAInlI9pwa3XYniCgQtkrAFfLXkiFo1jaXpo\ |
| 333 | 333 | +jUs6b5Z/K4siDu5RPUFADs="); |
| 334 | 334 | } |
| 335 | 335 | div.filetreeage { |
| 336 | 336 | display: table-cell; |
| 337 | - padding-left: 3em; | |
| 337 | + padding-left: 1.5em; | |
| 338 | + text-align: right; | |
| 339 | + width: 8em; | |
| 340 | +} | |
| 341 | +div.filetreesize { | |
| 342 | + display: table-cell; | |
| 343 | + padding-left: 1em; | |
| 338 | 344 | text-align: right; |
| 345 | + width: 7em; | |
| 339 | 346 | } |
| 340 | 347 | div.filetreeline:hover { |
| 341 | 348 | background-color: #eee; |
| 342 | 349 | } |
| 343 | 350 | table.login_out { |
| 344 | 351 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -332,12 +332,19 @@ | |
| 332 | v\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAInlI9pwa3XYniCgQtkrAFfLXkiFo1jaXpo\ |
| 333 | +jUs6b5Z/K4siDu5RPUFADs="); |
| 334 | } |
| 335 | div.filetreeage { |
| 336 | display: table-cell; |
| 337 | padding-left: 3em; |
| 338 | text-align: right; |
| 339 | } |
| 340 | div.filetreeline:hover { |
| 341 | background-color: #eee; |
| 342 | } |
| 343 | table.login_out { |
| 344 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -332,12 +332,19 @@ | |
| 332 | v\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAInlI9pwa3XYniCgQtkrAFfLXkiFo1jaXpo\ |
| 333 | +jUs6b5Z/K4siDu5RPUFADs="); |
| 334 | } |
| 335 | div.filetreeage { |
| 336 | display: table-cell; |
| 337 | padding-left: 1.5em; |
| 338 | text-align: right; |
| 339 | width: 8em; |
| 340 | } |
| 341 | div.filetreesize { |
| 342 | display: table-cell; |
| 343 | padding-left: 1em; |
| 344 | text-align: right; |
| 345 | width: 7em; |
| 346 | } |
| 347 | div.filetreeline:hover { |
| 348 | background-color: #eee; |
| 349 | } |
| 350 | table.login_out { |
| 351 |