diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 6398f4161b..741a85bdb2 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -290,6 +290,7 @@ #define SD_FINISHED_STEPPERRELEASE true //if sd support and the file is finished: disable steppers? #define SD_FINISHED_RELEASECOMMAND "M84 X Y Z E" // You might want to keep the z enabled so your bed stays in place. +#define SDCARD_SORT_ALPHA // Sort in ASCII order by pre-reading the folder and making a lookup table! #define SDCARD_RATHERRECENTFIRST //reverse file order of sd card menu display. Its sorted practically after the file system block order. // if a file is deleted, it frees a block. hence, the order is not purely chronological. To still have auto0.g accessible, there is again the option to do that. // using: diff --git a/Marlin/SdFatConfig.h b/Marlin/SdFatConfig.h index 710b1f7924..39ef381300 100644 --- a/Marlin/SdFatConfig.h +++ b/Marlin/SdFatConfig.h @@ -111,10 +111,12 @@ uint8_t const SOFT_SPI_SCK_PIN = 13; /** * Defines for long (vfat) filenames */ +/** Number of UTF-16 characters per entry */ +#define FILENAME_LENGTH 13 /** Number of VFAT entries used. Every entry has 13 UTF-16 characters */ #define MAX_VFAT_ENTRIES (2) /** Total size of the buffer used to store the long filenames */ -#define LONG_FILENAME_LENGTH (13*MAX_VFAT_ENTRIES+1) +#define LONG_FILENAME_LENGTH (FILENAME_LENGTH*MAX_VFAT_ENTRIES+1) #endif // SdFatConfig_h diff --git a/Marlin/cardreader.cpp b/Marlin/cardreader.cpp index d2fb418fba..862ed38475 100644 --- a/Marlin/cardreader.cpp +++ b/Marlin/cardreader.cpp @@ -11,6 +11,10 @@ CardReader::CardReader() { + #if SORT_USES_MORE_RAM + sortnames = NULL; + sort_count = 0; + #endif filesize = 0; sdpos = 0; sdprinting = false; @@ -53,15 +57,15 @@ char *createFilename(char *buffer,const dir_t &p) //buffer>12characters void CardReader::lsDive(const char *prepend,SdFile parent) { dir_t p; - uint8_t cnt=0; + uint8_t cnt=0; - while (parent.readDir(p, longFilename) > 0) + while (parent.readDir(p, diveFilename) > 0) { if( DIR_IS_SUBDIR(&p) && lsAction!=LS_Count && lsAction!=LS_GetFilename) // hence LS_SerialPrint { - char path[13*2]; - char lfilename[13]; + char path[FILENAME_LENGTH*2]; + char lfilename[FILENAME_LENGTH]; createFilename(lfilename,p); path[0]=0; @@ -87,25 +91,22 @@ void CardReader::lsDive(const char *prepend,SdFile parent) } lsDive(path,dir); //close done automatically by destructor of SdFile - - } else { if (p.name[0] == DIR_NAME_FREE) break; if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.'|| p.name[0] == '_') continue; - if (longFilename[0] != '\0' && - (longFilename[0] == '.' || longFilename[0] == '_')) continue; + if (diveFilename[0] != '\0' && + (diveFilename[0] == '.' || diveFilename[0] == '_')) continue; if ( p.name[0] == '.') { if ( p.name[1] != '.') continue; } - + if (!DIR_IS_FILE_OR_SUBDIR(&p)) continue; filenameIsDir=DIR_IS_SUBDIR(&p); - - + if(!filenameIsDir) { if(p.name[8]!='G') continue; @@ -124,10 +125,8 @@ void CardReader::lsDive(const char *prepend,SdFile parent) } else if(lsAction==LS_GetFilename) { - if(cnt==nrFiles) - return; + if (cnt == nrFiles) return; cnt++; - } } } @@ -136,9 +135,6 @@ void CardReader::lsDive(const char *prepend,SdFile parent) void CardReader::ls() { lsAction=LS_SerialPrint; - if(lsAction==LS_Count) - nrFiles=0; - root.rewind(); lsDive("",root); } @@ -177,6 +173,9 @@ void CardReader::initsd() } workDir=root; curDir=&root; + #ifdef SDCARD_SORT_ALPHA + presort(); + #endif /* if(!workDir.openRoot(&volume)) { @@ -193,8 +192,10 @@ void CardReader::setroot() SERIAL_ECHOLNPGM(MSG_SD_WORKDIR_FAIL); }*/ workDir=root; - curDir=&workDir; + #ifdef SDCARD_SORT_ALPHA + presort(); + #endif } void CardReader::release() { @@ -235,7 +236,7 @@ void CardReader::getAbsFilename(char *t) while(*t!=0 && cnt< MAXPATHNAMELENGTH) {t++;cnt++;} //crawl counter forward. } - if(cnt0 && dirname_end>dirname_start) { - char subdirname[13]; + char subdirname[FILENAME_LENGTH]; strncpy(subdirname, dirname_start, dirname_end-dirname_start); subdirname[dirname_end-dirname_start]=0; SERIAL_ECHOLN(subdirname); @@ -401,7 +402,7 @@ void CardReader::removeFile(char* name) //SERIAL_ECHO("end :");SERIAL_ECHOLN((int)(dirname_end-name)); if(dirname_end>0 && dirname_end>dirname_start) { - char subdirname[13]; + char subdirname[FILENAME_LENGTH]; strncpy(subdirname, dirname_start, dirname_end-dirname_start); subdirname[dirname_end-dirname_start]=0; SERIAL_ECHOLN(subdirname); @@ -439,6 +440,9 @@ void CardReader::removeFile(char* name) SERIAL_PROTOCOLPGM("File deleted:"); SERIAL_PROTOCOLLN(fname); sdpos = 0; + #ifdef SDCARD_SORT_ALPHA + presort(); + #endif } else { @@ -577,7 +581,7 @@ void CardReader::chdir(const char * relpath) { SdFile newfile; SdFile *parent=&root; - + if(workDir.isOpen()) parent=&workDir; @@ -595,21 +599,156 @@ void CardReader::chdir(const char * relpath) workDirParents[0]=*parent; } workDir=newfile; + #ifdef SDCARD_SORT_ALPHA + presort(); + #endif } } void CardReader::updir() { - if(workDirDepth > 0) + if (workDirDepth > 0) { --workDirDepth; workDir = workDirParents[0]; - int d; for (int d = 0; d < workDirDepth; d++) workDirParents[d] = workDirParents[d+1]; + #ifdef SDCARD_SORT_ALPHA + presort(); + #endif } } +#ifdef SDCARD_SORT_ALPHA + +/** + * Get the name of a file in the current directory by sort-index + */ +void CardReader::getfilename_sorted(const uint8_t nr) { + #if SORT_USES_MORE_RAM + getfilename(nr < sort_count ? sort_order[nr] : nr); + #else + getfilename(nr < SORT_LIMIT ? sort_order[nr] : nr); + #endif +} + +/** + * Read all the files and produce a sort key + * + * We can do this in 3 ways... + * - Minimal RAM: Read two filenames at a time sorting along... + * - Some RAM: Buffer the directory and return filenames from RAM + * - Some RAM: Buffer the directory just for this sort + */ +void CardReader::presort() +{ + #if SORT_USES_MORE_RAM + flush_presort(); + #endif + + uint16_t fileCnt = getnrfilenames(); + if (fileCnt > 0) { + + if (fileCnt > SORT_LIMIT) fileCnt = SORT_LIMIT; + + #if SORT_USES_MORE_RAM + sortnames = malloc(fileCnt * sizeof(char*)); + sort_count = fileCnt; + #elif SORT_USES_RAM + char *sortnames[fileCnt]; + #if FOLDER_SORTING != 0 + uint8_t isdir[fileCnt]; + #endif + #else + char sortname[LONG_FILENAME_LENGTH+1]; + #endif + + if (fileCnt > 1) { + + // Init sort order [and get filenames] + for (int i=0; i 0) : isdir[FOLDER_SORTING > 0 ? o1 : o2]; + #else + cmp = strcasecmp(sortnames[o1], sortnames[o2]) > 0); + #endif + #else + getfilename(o1); + #if FOLDER_SORTING != 0 + bool dir1 = filenameIsDir; + #endif + char *name = diveFilename[0] ? diveFilename : filename; + strcpy(sortname, name); + getfilename(o2); + name = diveFilename[0] ? diveFilename : filename; + #if FOLDER_SORTING != 0 + cmp = (dir1 == filenameIsDir) ? (strcasecmp(sortname, name) > 0) : (FOLDER_SORTING > 0 ? dir1 : !dir1); + #else + cmp = strcasecmp(sortname, name) > 0); + #endif + #endif + if (cmp) { + // SERIAL_ECHOPGM("Swap "); + // SERIAL_ECHOLN(sortnames[o1]); + // SERIAL_ECHOPGM(" for "); + // SERIAL_ECHOLN(sortnames[o2]); + sort_order[s1] = o2; + sort_order[s2] = o1; + didSwap = true; + } + } + if (!didSwap) break; + } + + #if SORT_USES_RAM && !SORT_USES_MORE_RAM + for (int i=0; i < fileCnt; ++i) free(sortnames[i]); + #endif + } + else { + sort_order[0] = 0; + } + + } +} + +void CardReader::flush_presort() { + #if SORT_USES_MORE_RAM + if (sort_count > 0) { + for (int i=0; i < sort_count; ++i) { + free(sortnames[i]); + sort_order[i] = i; + } + free(sortnames); + sortnames = NULL; + sort_count = 0; + } + #else + for (int i=SORT_LIMIT; --i;) sort_order[i] = i; + #endif +} + +#endif void CardReader::printingHasFinished() { diff --git a/Marlin/cardreader.h b/Marlin/cardreader.h index 78f7148b1f..1d8d1b1fb6 100644 --- a/Marlin/cardreader.h +++ b/Marlin/cardreader.h @@ -3,7 +3,11 @@ #ifdef SDSUPPORT -#define MAX_DIR_DEPTH 10 +#define MAX_DIR_DEPTH 10 // Maximum folder depth +#define SORT_USES_RAM false // Buffer while sorting, else re-read from SD +#define SORT_USES_MORE_RAM false // Always keep the directory in RAM +#define SORT_LIMIT 256 // Maximum number of sorted items +#define FOLDER_SORTING -1 // -1=above 0=none 1=below #include "SdFile.h" enum LsAction {LS_SerialPrint,LS_Count,LS_GetFilename}; @@ -39,6 +43,12 @@ public: void updir(); void setroot(); +#ifdef SDCARD_SORT_ALPHA + void presort(); + void flush_presort(); + void getfilename_sorted(const uint8_t nr); +#endif + FORCE_INLINE bool isFileOpen() { return file.isOpen(); } FORCE_INLINE bool eof() { return sdpos>=filesize ;}; @@ -51,19 +61,27 @@ public: bool saving; bool logging; bool sdprinting ; - bool cardOK ; - char filename[13]; - char longFilename[LONG_FILENAME_LENGTH]; + bool cardOK; + char filename[FILENAME_LENGTH]; + char diveFilename[LONG_FILENAME_LENGTH]; bool filenameIsDir; int lastnr; //last number of the autostart; private: SdFile root,*curDir,workDir,workDirParents[MAX_DIR_DEPTH]; uint16_t workDirDepth; +#ifdef SDCARD_SORT_ALPHA + #if SORT_USES_MORE_RAM + uint16_t sort_count; + char **sortnames; + #else + uint8_t sort_order[SORT_LIMIT]; + #endif +#endif Sd2Card card; SdVolume volume; SdFile file; #define SD_PROCEDURE_DEPTH 1 - #define MAXPATHNAMELENGTH (13*MAX_DIR_DEPTH+MAX_DIR_DEPTH+1) + #define MAXPATHNAMELENGTH (FILENAME_LENGTH*MAX_DIR_DEPTH+MAX_DIR_DEPTH+1) uint8_t file_subcall_ctr; uint32_t filespos[SD_PROCEDURE_DEPTH]; char filenames[SD_PROCEDURE_DEPTH][MAXPATHNAMELENGTH]; diff --git a/Marlin/ultralcd.cpp b/Marlin/ultralcd.cpp index 734c859d02..89cec4c48f 100644 --- a/Marlin/ultralcd.cpp +++ b/Marlin/ultralcd.cpp @@ -972,9 +972,9 @@ void lcd_sdcard_menu() card.getWorkDirName(); if(card.filename[0]=='/') { -#if SDCARDDETECT == -1 + #if SDCARDDETECT == -1 MENU_ITEM(function, LCD_STR_REFRESH MSG_REFRESH, lcd_sd_refresh); -#endif + #endif }else{ MENU_ITEM(function, LCD_STR_FOLDER "..", lcd_sd_updir); } @@ -983,16 +983,23 @@ void lcd_sdcard_menu() { if (_menuItemNr == _lineNr) { - #ifndef SDCARD_RATHERRECENTFIRST - card.getfilename(i); + #if defined(SDCARD_RATHERRECENTFIRST) && !defined(SDCARD_SORT_ALPHA) + int nr = fileCnt-1-i; #else - card.getfilename(fileCnt-1-i); + int nr = i; #endif - if (card.filenameIsDir) - { - MENU_ITEM(sddirectory, MSG_CARD_MENU, card.filename, card.longFilename); - }else{ - MENU_ITEM(sdfile, MSG_CARD_MENU, card.filename, card.longFilename); + + #ifdef SDCARD_SORT_ALPHA + card.getfilename_sorted(nr); + #else + card.getfilename(nr); + #endif + + if (card.filenameIsDir) { + MENU_ITEM(sddirectory, MSG_CARD_MENU, card.filename, card.diveFilename); + } + else { + MENU_ITEM(sdfile, MSG_CARD_MENU, card.filename, card.diveFilename); } }else{ MENU_ITEM_DUMMY(); @@ -1198,7 +1205,7 @@ void lcd_init() #endif // SR_LCD_2W_NL #endif//!NEWPANEL -#if defined (SDSUPPORT) && defined(SDCARDDETECT) && (SDCARDDETECT > 0) +#if defined(SDSUPPORT) && defined(SDCARDDETECT) && (SDCARDDETECT > 0) pinMode(SDCARDDETECT,INPUT); WRITE(SDCARDDETECT, HIGH); lcd_oldcardstatus = IS_SD_INSERTED;