Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 09:09 01 Aug 2025 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : CMM2: GetFileDialog Box

     Page 1 of 4    
Author Message
vegipete

Guru

Joined: 29/01/2013
Location: Canada
Posts: 1132
Posted: 05:52am 07 Oct 2020
Copy link to clipboard 
Print this post

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: Australia
Posts: 6283
Posted: 06:03am 07 Oct 2020
Copy link to clipboard 
Print this post

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: Australia
Posts: 6283
Posted: 06:48am 07 Oct 2020
Copy link to clipboard 
Print this post

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 Kingdom
Posts: 4311
Posted: 10:50am 07 Oct 2020
Copy link to clipboard 
Print this post

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: Belgium
Posts: 255
Posted: 01:11pm 07 Oct 2020
Copy link to clipboard 
Print this post

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: Germany
Posts: 1593
Posted: 06:36pm 07 Oct 2020
Copy link to clipboard 
Print this post

@Pete

good job! A useful contribution, thanks!

Michael
causality ≠ correlation ≠ coincidence
 
elk1984

Senior Member

Joined: 11/07/2020
Location: United Kingdom
Posts: 228
Posted: 07:53pm 07 Oct 2020
Copy link to clipboard 
Print this post

  vegipete said  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?


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: Germany
Posts: 1593
Posted: 05:57pm 08 Oct 2020
Copy link to clipboard 
Print this post

@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: Canada
Posts: 1132
Posted: 06:19pm 08 Oct 2020
Copy link to clipboard 
Print this post

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: Germany
Posts: 1593
Posted: 08:13pm 08 Oct 2020
Copy link to clipboard 
Print this post

  vegipete said  I guess that wall of random characters as a backdrop to the dialog box is a bit frightening. I'll make an improvement.



You could also take a look at the "Lorem ipsum" texts.
  vegipete said  Showing directory names only seems reasonable. I guess I have to make the spec$ work. Then setting spec$ = "DIR" would display only directories.

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 Kingdom
Posts: 228
Posted: 08:44pm 08 Oct 2020
Copy link to clipboard 
Print this post

  twofingers said  
  vegipete said  I guess that wall of random characters as a backdrop to the dialog box is a bit frightening. I'll make an improvement.



You could also take a look at the "Lorem ipsum" texts.
  vegipete said  Showing directory names only seems reasonable. I guess I have to make the spec$ work. Then setting spec$ = "DIR" would display only directories.

spec$ = "DIR" doesn't sound like the best idea (IMHO, sorry!). I would use a 3rd optional parameter.
Best regards
Michael


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  ?).  Maybe I'm overthinking.
 
twofingers

Guru

Joined: 02/06/2014
Location: Germany
Posts: 1593
Posted: 08:59pm 08 Oct 2020
Copy link to clipboard 
Print this post

@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: Canada
Posts: 1132
Posted: 09:15pm 08 Oct 2020
Copy link to clipboard 
Print this post

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: Australia
Posts: 6283
Posted: 09:38pm 08 Oct 2020
Copy link to clipboard 
Print this post

  vegipete said  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?

current directory has traditionally been a single dot. "."

Jim
VK7JH
MMedit
 
vegipete

Guru

Joined: 29/01/2013
Location: Canada
Posts: 1132
Posted: 09:41pm 08 Oct 2020
Copy link to clipboard 
Print this post

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 Kingdom
Posts: 228
Posted: 11:06pm 08 Oct 2020
Copy link to clipboard 
Print this post

  vegipete said  Traditionally, yes, but does the average person now know that?


Depends if I'm average  
 
mkopack73
Senior Member

Joined: 03/07/2020
Location: United States
Posts: 261
Posted: 12:30am 09 Oct 2020
Copy link to clipboard 
Print this post

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: Germany
Posts: 1593
Posted: 12:47am 09 Oct 2020
Copy link to clipboard 
Print this post

@Pete
  vegipete said  If you are selecting a directory, does it matter if you can see the files that are already in that directory?

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.
  vegipete said  What if I made the top item <Here>, meaning select the current directory?

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: Canada
Posts: 1132
Posted: 08:06am 09 Oct 2020
Copy link to clipboard 
Print this post

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: Germany
Posts: 1593
Posted: 09:23am 09 Oct 2020
Copy link to clipboard 
Print this post

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    
Print this page
The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025