![]() |
Forum Index : Microcontroller and PC projects : CMM2: GetFileDialog Box
Page 1 of 4 ![]() ![]() |
|||||
Author | Message | ||||
vegipete![]() Guru ![]() Joined: 29/01/2013 Location: CanadaPosts: 1132 |
There has been a nice collection of utility programs created in the last little while but one aspect of them has put me off. You type run, and the program asks: Enter file name: I'm expected to know the file name, and type it in? Are you mad? What is this, the 70s? Here is my solution: ![]() The GetFileName function should be very easy to add to programs, lets the user select a file from those on the SD card, traversing directories in the process, and returns the full path of the selected file in a string. (An empty string indicates Cancel.) The number of lines of files shown in the dialog is set in the function call. Many other aspects can be altered easily in the code. The dialog box respects whatever is on screen before hand and redraws the pre-existing image when it quits. There is a short demo program to show it off. The demo program uses MODE 1,8 but other modes should work just as well - it needs a horizontal resolution of at least 300 pixels so modes 5 and 6 might look chopped. Give it a try, and use it yourself if it's helpful. Let me know about problems, or requests or whatever. ' GetFileName Demonstration Program ' Shows usage for the GetFileName dialog function. ' ' by vegipete, October 2020 '=================== ' required setup for function DIRCOUNT = 20 FILCOUNT = 255 NAMELENGTH = 64 dim dir_dirs$(DIRCOUNT) length NAMELENGTH ' store list of directories dim dir_fils$(FILCOUNT) length NAMELENGTH ' store list of files dim dir_notroot ' used internally to indicate root directory or not ' end of setup for funtion '=================== mode 1,8 cls ' Fill the screen with some junk for i = 1 to 3000 : ? chr$(32 + int(rnd * 96));: next a$ = GetFileName(15,"0") if a$ = "" then ? @(0,400) "No file selected" else ? @(0,400) "File selected: " a$ endif end '***************************************************************** ' Function GetFileName(height,spec$) ' ' version 1.0 vegipete, Oct 2020 ' ' This funtion displays a centered dialog box on the screen, allows ' the user to choose a file and returns the full path of the chosen ' file. The underlying screen is restored when the dialog closes. ' UP and DOWN arrows to select, ENTER to choose selection ' ESC to cancel, LEFT arrow to go up directory ' ' Input: height: number of directory items to list vertically ' spec$: currently unused, hopefully later it will allow file filtering ' ' Output: string containing full path of file chosen, or "" if nothing ' Note: the directory part of the path will be capitalized. This is just ' how the CWD$ function works. Fortunately, MMBasic is case insensitive. ' ' The following global variables should be declared before use: ' DIRCOUNT = 100 ' number of directories, adjust as required ' FILCOUNT = 255 ' number of files in directory, adjust as required ' NAMELENGTH = 64 ' max length of file and directory names ' dim dir_dirs$(DIRCOUNT) length NAMELENGTH ' store list of directories ' dim dir_fils$(FILCOUNT) length NAMELENGTH ' store list of files ' dim dir_notroot ' used internally to indicate root directory or not ' ' Routines Used: (included below) ' sub ReadDir ' reads current directory into the above arrays ' sub ListDir(first, nlines, hilite) ' shows a portion of the current directory ' function GetFileName(height,spec$) as string ' dialog box dimensions d_shadow = &h404020 ' dark sort-of gold d_frame = &hA0A040 ' sort-of gold d_back = &h101010 ' really dark grey d_lines = height d_height = 50 + (d_lines - 1) * MM.INFO(FONTHEIGHT) d_width = 300 d_x = (MM.HRES - d_width)/2 d_y = (MM.VRES - d_height)/2 ' save starting directory d_startdir$ = cwd$ ' save underlying screen image in buffer #64 blit read 64, d_x, d_y, d_width, d_height ' draw dialog box rbox d_x + 7, d_y + 7, d_width - 8, d_height - 8, 10, d_shadow, d_shadow rbox d_x , d_y , d_width - 8, d_height - 8, 10, d_frame, d_frame rbox d_x + 5, d_y + 20, d_width - 18, d_height - 32, 5, d_back, d_back dir_dirs$(0)="Select File... "+chr$(146)+chr$(147)+chr$(149)+"/Enter/Esc" 'temp text d_x + 10, d_y + 6, dir_dirs$(0), "LT", 1, 1, 0, -1 '-------------------- ReadDir ' fill dir_dirs$() and dir_fils$() d_top_item = 1 d_sel_item = 1 d_top_last = val(dir_dirs$(0)) + val(dir_fils$(0)) - d_lines + 1 ListDir(d_top_item, d_lines, d_sel_item) ' populate the dialog box do d_k = asc(inkey$) d_changed = 0 select case d_k case 27 ' ESC GetFileName$ = "" ' Cancel so return blank exit do case 128 ' UP arrow if d_sel_item = 1 then ' is the top item selected? if d_top_item > 1 then ' at top of list? d_top_item = d_top_item - 1 ' no so shift list up one d_changed = 1 endif else d_sel_item = d_sel_item - 1 ' shift selection up one d_changed = 1 endif case 129 ' DOWN arrow if d_sel_item = d_lines then ' is the bottom item selected? if d_top_item < d_top_last then ' at bottom of list? d_top_item = d_top_item + 1 ' no so shift list down one d_changed = 1 endif else if d_sel_item < val(dir_dirs$(0)) + val(dir_fils$(0)) then ' don't shift down past last item d_sel_item = d_sel_item + 1 ' shift selection down one d_changed = 1 endif case 130 ' LEFT Arrow - directory up if not root if dir_notroot then ' in a sub-directory? chdir ".." 'directory up chosen ReadDir ' read new directory d_top_item = 1 d_sel_item = 1 d_top_last = val(dir_dirs$(0)) + val(dir_fils$(0)) - d_lines + 1 d_changed = 1 endif case 13 ' ENTER d_chosen = d_top_item + d_sel_item - 1 if dir_notroot and (d_chosen = 1) then ' top item of a sub-directory? chdir ".." 'directory up chosen ReadDir ' read new directory d_top_item = 1 d_sel_item = 1 d_top_last = val(dir_dirs$(0)) + val(dir_fils$(0)) - d_lines + 1 d_changed = 1 elseif d_chosen <= val(dir_dirs$(0)) then ' item number in directory range? if right$(cwd$,1) = "/" then chdir cwd$ + dir_dirs$(d_chosen) ' tunnel down a directory else chdir cwd$ + "/" + dir_dirs$(d_chosen) ' tunnel down a directory endif ReadDir ' read new directory d_top_item = 1 d_sel_item = 1 d_top_last = val(dir_dirs$(0)) + val(dir_fils$(0)) - d_lines + 1 d_changed = 1 else ' Yahoo! A filename has been chosen d_chosen = d_chosen - val(dir_dirs$(0)) if right$(cwd$,1) = "/" then GetFileName$ = cwd$ + dir_fils$(d_chosen) ' filename at root level else GetFileName$ = cwd$ + "/" + dir_fils$(d_chosen) ' filename deeper endif ' Note: cwd$ returns all uppercase exit do endif end select if d_changed then ' something changed so redisplay directory list ListDir(d_top_item, d_lines, d_sel_item) endif loop '-------------------- ' restore original screen image box d_x, d_y, d_width, d_height, 1, 0, 0 ' must clear to black first blit write 64, d_x, d_y ' now restore all non-black pixels blit close 64 ' restore starting directory chdir d_startdir$ end function '***************************************************************** ' Read everything in the current directory sub ReadDir local item_cnt, i for i = 1 to DIRCOUNT dir_dirs$(i) = "" ' clear the array next i for i = 1 to FILCOUNT dir_fils$(i) = "" ' clear the array next i ' check for root directory dir_notroot = 0 if cwd$ <> "A:/" then ' root directory? dir_notroot = 1 endif ' read directories first dir_dirs$(0) = "" item_cnt = 1 dir_dirs$(item_cnt) = left$(Dir$("*", DIR),NAMELENGTH) ' WARNING - possible truncation Do While dir_dirs$(item_cnt) <> "" and item_cnt < DIRCOUNT - 1 If dir_dirs$(item_cnt) <> "." Then item_cnt = item_cnt + 1 ' ignore "." dir_dirs$(item_cnt) = Dir$() Loop if dir_dirs$(item_cnt) = "" then item_cnt = item_cnt - 1 ' Sort directories and shift non-blank entries to front of array Sort dir_dirs$() if dir_notroot then dir_dirs$(1) = ".." ' not root so add directory up entry for i = 1 to item_cnt dir_dirs$(i+dir_notroot) = dir_dirs$(DIRCOUNT-item_cnt+i) next i dir_dirs$(0) = str$(item_cnt+dir_notroot) ' store number of items ' now read files dir_fils$(0) = "" item_cnt = 1 dir_fils$(item_cnt) = left$(Dir$("*", FILE),NAMELENGTH) ' WARNING - possible truncation Do While dir_fils$(item_cnt) <> "" and item_cnt < FILCOUNT - 1 If dir_fils$(item_cnt) <> "." Then item_cnt = item_cnt + 1 ' ignore "." dir_fils$(item_cnt) = Dir$() Loop if dir_fils$(item_cnt) = "" then item_cnt = item_cnt - 1 ' Sort files and shift non-blank entries to front of array Sort dir_fils$() for i = 1 to item_cnt dir_fils$(i) = dir_fils$(FILCOUNT-item_cnt+i) next i dir_fils$(0) = str$(item_cnt) ' store number of items end sub '***************************************************************** ' Display (part of) directory ' Show 'nlines' number of items, starting with item 'first', ' hilite given item ' The magic number "33" should be altered to correspond to the magic ' number "300" used above for d_width. sub ListDir(first, nlines, hilite) local i for i = 0 to nlines - 1 item = first + i if item > val(dir_dirs$(0)) then d_txt$ = dir_fils$(item - val(dir_dirs$(0))) else d_txt$ = "<DIR> " + dir_dirs$(item) endif if len(d_txt$) > 33 then d_txt$ = left$(d_txt$,32) + chr$(148) d_txt$ = left$(d_txt$ + space$(33),33) if i = hilite - 1 then text d_x+14, d_y+22+i*MM.INFO(FONTHEIGHT), d_txt$,"LT",1,1,d_back,d_frame else text d_x+14, d_y+22+i*MM.INFO(FONTHEIGHT), d_txt$,"LT",1,1,&hFFFFFF,d_back endif next i end sub '***************************************************************** Edited 2020-10-07 15:55 by vegipete Visit Vegipete's *Mite Library for cool programs. |
||||
TassyJim![]() Guru ![]() Joined: 07/08/2011 Location: AustraliaPosts: 6283 |
I like it. Now to add my mouse wheel for selection... I wish I had read the code before I ran it. A screen full of garbage was a surprise! Jim Edited 2020-10-07 16:06 by TassyJim VK7JH MMedit |
||||
TassyJim![]() Guru ![]() Joined: 07/08/2011 Location: AustraliaPosts: 6283 |
Two functions, a couple of globals and one line added to give me mouse wheel selection: ' wii_mouse globals dim integer wiiX, wiiY, wiiB, wiiMove, wiiClick, wiiPort = 1 wii classic open wiiPort, checkWii do d_k = asc(inkey$) d_changed = 0 if wiiClick = 1 then d_k = wiikey() ' added this line function wiikey() as integer ' translate wheel roll and click to keyboard local integer wiiL, wiiM, wiiR, wiiW if wiiClick = 1 then wiiR = wiiB and 1 ' wii buttons wiiM = (wiiB >> 2) and 1 wiiL = (wiiB >> 4) and 1 wiiW = ((wiiB >> 1) and 1) - ((wiiB >> 3) and 1) if wiiW < 0 then wiikey = 128 elseif wiiW > 0 then wiikey = 129 elseif wiiM = 1 then wiikey = 13 endif wiiClick = 0 endif end function sub checkWii ' called with any wii activity local integer deltaB, deltaX, deltaY deltaX = classic(LX,wiiPort) - 124 ' adjust as required to remove drift deltaY = classic(LY,wiiPort) - 124 deltaB = classic(B ,wiiPort) if deltaX <>0 or deltaY <> 0 then wiiMove = 1 wiiX = wiiX + deltaX wiiY = wiiY - deltaY endif 'if wiiB <> deltaB then if deltaB <> 0 then wiiClick = 1 wiiB = deltaB endif end sub It could easily be changed to suit any wii classic buttons. Jim VK7JH MMedit |
||||
thwill![]() Guru ![]() Joined: 16/09/2019 Location: United KingdomPosts: 4311 |
Nice work Pete, I can see this or derivations of it appearing in many future contributions. Tom MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures |
||||
epsilon![]() Senior Member ![]() Joined: 30/07/2020 Location: BelgiumPosts: 255 |
Coming soon to a Hex/Mem/TxtEditor near you! I haven't played with it yet. Is there an option to create new files? Epsilon CMM2 projects |
||||
twofingers![]() Guru ![]() Joined: 02/06/2014 Location: GermanyPosts: 1593 |
@Pete good job! ![]() Michael causality ≠correlation ≠coincidence |
||||
elk1984![]() Senior Member ![]() Joined: 11/07/2020 Location: United KingdomPosts: 228 |
Thank you - couldn't agree more in fact, it was on the list for every project I've got in progress right now. |
||||
twofingers![]() Guru ![]() Joined: 02/06/2014 Location: GermanyPosts: 1593 |
@Pete I changed the demo text to: for i = 1 to 100:PRINT "The quick brown fox jumps over the lazy dog";:next I think that looks better, less confusing. ![]() Can you change the code to have folder names only? It would be useful to improve the epsilons archive program (MAR). Kind regards Michael causality ≠correlation ≠coincidence |
||||
vegipete![]() Guru ![]() Joined: 29/01/2013 Location: CanadaPosts: 1132 |
I guess that wall of random characters as a backdrop to the dialog box is a bit frightening. I'll make an improvement. Showing directory names only seems reasonable. I guess I have to make the spec$ work. Then setting spec$ = "DIR" would display only directories. Returning a directory name instead of entering a directory would certainly be useful. Then for example, a hex file editor could directly edit a directory - dangerous, but possible. But something would have to change. Perhaps [Enter] returns the selected item and [->] (right arrow) tunnels deeper into the selected sub-directory? But then, since [<-] backs out one directory, perhaps the item <..> is unnecessary? What do people think? Visit Vegipete's *Mite Library for cool programs. |
||||
twofingers![]() Guru ![]() Joined: 02/06/2014 Location: GermanyPosts: 1593 |
![]() You could also take a look at the "Lorem ipsum" texts. ![]() spec$ = "DIR" doesn't sound like the best idea (IMHO, sorry!). I would use a 3rd optional parameter. Best regards Michael causality ≠correlation ≠coincidence |
||||
elk1984![]() Senior Member ![]() Joined: 11/07/2020 Location: United KingdomPosts: 228 |
It is great work, but spec does sound like a file/dir spec. Intuitively *.TXT means anything that matches irrespective of directory or file. Directories only I think is a lot closer to an attribute switch (does the CMM2 support read only, hidden and all that stuff ![]() |
||||
twofingers![]() Guru ![]() Joined: 02/06/2014 Location: GermanyPosts: 1593 |
@Pete, I think "<DIR>" would be a option to use. You can have a folder named "DIR" but not "<DIR>" (The path name format is invalid). Michael causality ≠correlation ≠coincidence |
||||
vegipete![]() Guru ![]() Joined: 29/01/2013 Location: CanadaPosts: 1132 |
If you are selecting a directory, does it matter if you can see the files that are already in that directory? What if I made the top item <Here>, meaning select the current directory? Visit Vegipete's *Mite Library for cool programs. |
||||
TassyJim![]() Guru ![]() Joined: 07/08/2011 Location: AustraliaPosts: 6283 |
What if I made the top item <Here>, meaning select the current directory? current directory has traditionally been a single dot. "." Jim VK7JH MMedit |
||||
vegipete![]() Guru ![]() Joined: 29/01/2013 Location: CanadaPosts: 1132 |
Traditionally, yes, but does the average person now know that? Visit Vegipete's *Mite Library for cool programs. |
||||
elk1984![]() Senior Member ![]() Joined: 11/07/2020 Location: United KingdomPosts: 228 |
Depends if I'm average ![]() |
||||
mkopack73 Senior Member ![]() Joined: 03/07/2020 Location: United StatesPosts: 261 |
Make it an .inc so everyone can easily pull it in and use it if they need it! |
||||
twofingers![]() Guru ![]() Joined: 02/06/2014 Location: GermanyPosts: 1593 |
@Pete Maybe Ruben (epsilon) should answer this, but I (IMHO) would say it should show ALL files AND directories, but only directories can be selected. A functionality like DOS "Dir /AD" is also useful. Not a bad idea - I like it-, but a non-standard way of doing it. I would think that picking the directory from the next higher level is good enough. That would mean that the function remains unchanged, but only directories can be selected if the name is displayed and explicitly selected. In the sense of keeping things foolproof. Just my 2c! Maybe we should read the opinions of others about it. Michael causality ≠correlation ≠coincidence |
||||
vegipete![]() Guru ![]() Joined: 29/01/2013 Location: CanadaPosts: 1132 |
Here is a changed version. The arrow keys move the selection around. Left arrow goes up a directory. Right arrow enters a directory. [Enter] selects the highlighted item (directory or file) [Esc] cancels. There is a bit more fiddling to do, but please test this for usability and clarity. Also, I made the background perhaps less alarming than a wall of random characters. (Unless you are afraid of bees.) GetFile11.zip Visit Vegipete's *Mite Library for cool programs. |
||||
twofingers![]() Guru ![]() Joined: 02/06/2014 Location: GermanyPosts: 1593 |
Hi Pete, The background of the v1.1 looks much better now! ![]() ![]() .. and I'm not a drone! ![]() Choosing a directory now works as required, but I would prefer a filename blocking option. I think the GetFileName function should have a parameter to make only folders selectable. Thank you for your work so far! ![]() Michael causality ≠correlation ≠coincidence |
||||
Page 1 of 4 ![]() ![]() |
![]() |
![]() |
The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |