QB64 Phoenix Edition
Need a little help with updating a FileSelect function. - Printable Version

+- QB64 Phoenix Edition (https://qb64phoenix.com/forum)
+-- Forum: QB64 Rising (https://qb64phoenix.com/forum/forumdisplay.php?fid=1)
+--- Forum: Code and Stuff (https://qb64phoenix.com/forum/forumdisplay.php?fid=3)
+---- Forum: Help Me! (https://qb64phoenix.com/forum/forumdisplay.php?fid=10)
+---- Thread: Need a little help with updating a FileSelect function. (/showthread.php?tid=3115)

Pages: 1 2


Need a little help with updating a FileSelect function. - Dav - 10-11-2024

I'm updating my old FileSelect function to use _Files$ instead of SHELLing and making temp files to get file/dir info.  Working OK doing that (I think) but have a couple a questions using CHDIR with it.

If there a way to see if a directory is accessible before trying to navigate to it?  When I try to navigate to a directory with permissions it errors out.  I'd like to not allow user to open those directories.  

Also, what is a good way (cross-platform) to see if the user is at the root directory?

Here's the code I'm working on.  Thanks.

- Dav 

Code: (Select All)
'====================
'FILESELECT-FILES.BAS  v1.00
'====================
'A simple file selector box function using _FILES$.
'Coded by Dav for QB64-PE, OCT/2024

'Lists files in current directory in a scroll box.

'Use arrows, page up/down, home/end to navigate.
'ENTER selects highlighted filename, ESC cancels.

'Selecting a directory will navigate to that
'directory and list files under it.

'Background screen is preserved and restored.

'Works under windows & Linux (havent tested Mac).
'Works in text and graphical screen modes.
'

'=== DEMO FOLLOWS...

Screen _NewImage(700, 500, 32)
_ScreenMove _Middle

'=== draw a background

Cls , _RGB(32, 32, 32)
For x = 1 To _Width Step 3
    For y = 1 To _Height Step 3
        PSet (x, y), _RGB(Rnd * 255, Rnd * 255, Rnd * 255)
    Next
Next

Print "Use arrows to navigate to a filename.  "
Print "Press ENTER to select highlighted file."
Print "Press ESC to cancel and close file box."

'=== Define filebox colors here...

fsborder& = _RGB(255, 0, 0) 'filebox order color
fsfile& = _RGB(255, 255, 255) 'filename color
fsdir& = _RGB(255, 255, 64) 'directories color
fsback& = _RGB(64, 0, 0) 'Background color
fshigh& = _RGB(255, 255, 128) 'highlighted line color

'=== Ask user to select a file

a$ = FileSelect$(5, 15, 20, 55, "*", fsborder&, fsback&, fsfile&, fsdir&, fshigh&)

'=== Show results...

Print
If a$ <> "" Then
    Print "You selected: "; a$
Else
    Print "No file selected."
End If

End


Function FileSelect$ (y, x, y2, x2, Filespec$, fsborder&, fsback&, fsfile&, fsdir&, fshigh&)
    'FileSelect$ function by Dav, OCT/2024

    'This function returns a selected filename.

    'Show files in current directory in a scroll box.
    'Use arrows, page up/down, home/end to navigate.
    'ENTER selects highlighted filename, ESC cancels.
    'Selecting a directory will navigate to that dir
    'and list files under that directory.

    'y,x = top left of box
    'y2,x2 = bottom right of box
    'Filespec$ = spec of files to list in box
    'fsborder& = color of box border
    'fsback& = background color of file box.
    'fsfile& = color of filenames
    'fsdir& = color of directories
    'fshigh& = color of highlighted line
    '=================================================

    '=== save original place of cursor
    origy = CsrLin
    origx = Pos(1)

    '=== save colors
    fg& = _DefaultColor
    bg& = _BackgroundColor

    '=== Save whole screen
    Dim scr1 As _MEM, scr2 As _MEM
    scr1 = _MemImage(0): scr2 = _MemNew(scr1.SIZE)
    _MemCopy scr1, scr1.OFFSET, scr1.SIZE To scr2, scr2.OFFSET

    '========
    loadagain:
    '=======

    '=== get total file + dir count
    c = 0
    x$ = _Files$(Filespec$)
    Do
        If x$ = "" Then Exit Do
        c = c + 1
        x$ = _Files$
    Loop

    'if no files found, at least add 1 for ".."
    If c = 0 Then c = 1

    '=== make room for names
    ReDim FileNames$(0 To c)

    '=== first line always go up a directory
    FileNames$(0) = ".."
    LineCount = 1

    '=== get dir names first, add to Filename$ array
    x$ = _Files$(Filespec$)
    Do
        If x$ = "" Then Exit Do
        'skip "." and ".." from this list
        If x$ = "./" Or x$ = ".\" Then GoTo jump
        If x$ = "../" Or x$ = "..\" Then GoTo jump
        If Right$(x$, 1) = "/" Or Right$(x$, 1) = "\" Then
            FileNames$(LineCount) = "[" + Left$(x$, Len(x$) - 1) + "]"
            LineCount = LineCount + 1
        End If
        jump:
        x$ = _Files$
    Loop

    '=== get file names next, add to Filename$ array
    x$ = _Files$(Filespec$)
    Do
        If x$ = "" Then Exit Do
        'skip all dir this time
        If Right$(x$, 1) = "/" Or Right$(x$, 1) = "\" Then
            ' do nothing
        Else
            FileNames$(LineCount) = x$
            LineCount = LineCount + 1
        End If
        x$ = _Files$
    Loop


    '=== draw a box
    Color fsborder&
    For L = 0 To y2 + 1
        Locate y + L, x: Print String$(x2 + 4, Chr$(219));
    Next

    '=== show current working dir at top
    Color fsfile&, fsborder&
    CurDir$ = _CWD$
    '=== Shorten it is too long, for display purposes
    If Len(CurDir$) > x2 - x Then
        CurDir$ = Mid$(CurDir$, 1, x2 - x - 3) + "..."
    End If
    Locate y, x + 2: Print CurDir$;


    '=== scroll through list...
    top = 0: selection = 0

    Do

        For L = 0 To (y2 - 1)

            Locate (y + 1) + L, (x + 2)

            topl = top + L: If topl > LineCount Then topl = LineCount

            If topl = selection Then
                Color fsback&, fshigh& 'selected line
            Else
                Color fsfile&, fsback& 'regular
                '=== directories get a different color...
                If Mid$(FileNames$(topl), 1, 1) = "[" Then
                    Color fsdir&, fsback&
                End If
            End If
            Print Left$(FileNames$(topl) + String$(x2, " "), x2);
        Next

        '=== Get user input
        Do
            k$ = InKey$: _Limit 30
        Loop Until k$ <> ""

        Select Case k$

            Case Is = Chr$(0) + Chr$(72) 'Up arrow
                If selection > 0 Then selection = selection - 1
                If selection < top Then top = selection

            Case Is = Chr$(0) + Chr$(80) 'Down Arrow
                If selection < (LineCount - 1) Then selection = selection + 1
                If selection > (top + (y2 - 2)) Then top = selection - y2 + 1

            Case Is = Chr$(0) + Chr$(73) 'Page up
                top = top - y2
                selection = selection - y2
                If top < 0 Then top = 0
                If selection < 0 Then selection = 0

            Case Is = Chr$(0) + Chr$(81) 'Page Down
                top = top + y2
                selection = selection + y2
                If top >= LineCount - y2 Then top = LineCount - y2
                If top < 0 Then top = 0
                If selection >= LineCount Then selection = LineCount - 1

            Case Is = Chr$(0) + Chr$(71) 'Home
                top = 0: selection = 0

            Case Is = Chr$(0) + Chr$(79) 'End
                selection = LineCount - 1
                top = selection - y2 + 1
                If top < 0 Then top = 0

            Case Is = Chr$(27) ' ESC cancels
                FileSelect$ = ""
                Exit Do

            Case Is = Chr$(13) 'Enter

                '=== if .. then go up one dir
                If RTrim$(FileNames$(selection)) = ".." Then
                    cd$ = _CWD$
                    If InStr(_OS$, "WIN") Then
                        cd$ = Left$(cd$, _InStrRev(cd$, "\"))
                    Else
                        cd$ = Left$(cd$, _InStrRev(cd$, "/"))
                    End If
                    ChDir cd$
                    Erase FileNames$
                    GoTo loadagain
                End If

                'see if directory
                test$ = RTrim$(FileNames$(selection))
                If Left$(test$, 1) = "[" Then
                    test$ = Mid$(test$, 2, Len(test$) - 2)
                    ChDir test$
                    Erase FileNames$
                    GoTo loadagain
                Else
                    If InStr(_OS$, "WIN") Then
                        If Right$(_CWD$, 1) = "\" Then
                            C$ = _CWD$
                        Else
                            C$ = _CWD$ + "\"
                        End If
                    Else
                        If Right$(_CWD$, 1) = "/" Then
                            C$ = _CWD$
                        Else
                            C$ = _CWD$ + "/"
                        End If
                    End If

                    FileSelect$ = C$ + RTrim$(FileNames$(selection))
                    Exit Do

                End If

        End Select

    Loop

    _KeyClear

    '=== Restore the whole screen
    _MemCopy scr2, scr2.OFFSET, scr2.SIZE To scr1, scr1.OFFSET
    _MemFree scr1: _MemFree scr2

    '=== restore original y,x and color
    Locate origy, origx

    Color fg&, bg&

End Function



RE: Need a little help with updating a FileSelect function. - luke - 10-11-2024

In general the only sure way to check if a filesystem operation is allowed is to do it and be prepared to catch the error. You could maybe try change into every directory immediately to test each one and thus filter the list presented to the user, but I think it's more confusing to see a list that has some entries mysteriously missing. An "access denied" or similar message would be perfectly acceptable to me when I open a directory (cf Windows Explorer, most other file managers).

In any case you need the error handling whenever doing filesystem operations because you don't know what other programs are also modifying the disk concurrently.

At least in Unix systems the root directory has the special property that the parent ".." is also the root, so if you do CHDIR ".." and _CWD$ you must have been in the root. I assume that holds true for windows too. Or just have an OS conditional and check _CWD$ for "/" or "C:" (or is it "C:\"?).


RE: Need a little help with updating a FileSelect function. - bplus - 10-11-2024

Your code works for me, but do you know about _OpenFileDialog$ and _SaveFileDialog$?


RE: Need a little help with updating a FileSelect function. - Dav - 10-11-2024

@luke: Thank you very much!

@bplus: Thanks for testing. Yes, those are nice functions. I wanted a customized look.  Also thinking of making a clone of my old favorite filemanager from DOS a days, which listed files like this. I may go back to SHELL and temp files for that though, because I can get a lot more file info that way. I often thought it would be nice if there was a _FILEINFO function to retrieve file/dir details, things like if read only, last modified, permissions, date, etc.

- Dav


RE: Need a little help with updating a FileSelect function. - RhoSigma - 10-11-2024

Hi Dav,
below is the logic I use in my GuiTools, but it's windows only. I've no idea how to figure out if you're in the root under Linux/Mac as those don't use the ":" as far as I know. Also I'm not sure if those platforms behave the same way as Windows regarding the error numbers.

Code: (Select All)
'curr$ = the directory to check
IF NOT _DIREXISTS(curr$) THEN
IF LEN(curr$) = 3 AND RIGHT$(curr$, 2) = ":\" THEN 'is root ??
'permission denied or no media in (removeable) drive
ELSE
'path not found
END IF
ELSE
IF LEN(curr$) > 3 OR MID$(curr$, 2, 1) <> ":" THEN 'is not root or relative ??
oldCurr$ = _CWD$
ON ERROR GOTO internalHandler
lastErr% = 0
CHDIR curr$
ON ERROR GOTO 0
IF lastErr% = 0 THEN
'path is accessible
ELSEIF lastErr% = 76 THEN
'permission denied
END IF
CHDIR oldCurr$
END IF
END IF

internalHandler:
lastErr% = ERR
RESUME NEXT



RE: Need a little help with updating a FileSelect function. - Dav - 10-11-2024

(10-11-2024, 03:15 PM)RhoSigma Wrote: Hi Dav,
below is the logic I use in my GuiTools, but it's windows only. I've no idea how to figure out if you're in the root under Linux/Mac as those don't use the ":" as far as I know. Also I'm not sure if those platforms behave the same way as Windows regarding the error numbers.

Code: (Select All)
'curr$ = the directory to check
IF NOT _DIREXISTS(curr$) THEN
    IF LEN(curr$) = 3 AND RIGHT$(curr$, 2) = ":\" THEN 'is root ??
        'permission denied or no media in (removeable) drive
    ELSE
        'path not found
    END IF
ELSE
    IF LEN(curr$) > 3 OR MID$(curr$, 2, 1) <> ":" THEN 'is not root or relative ??
        oldCurr$ = _CWD$
        ON ERROR GOTO internalHandler
        lastErr% = 0
        CHDIR curr$
        ON ERROR GOTO 0
        IF lastErr% = 0 THEN
            'path is accessible
        ELSEIF lastErr% = 76 THEN
            'permission denied
        END IF
        CHDIR oldCurr$
    END IF
END IF

internalHandler:
lastErr% = ERR
RESUME NEXT

Thank you.  I can use this.

- Dav


RE: Need a little help with updating a FileSelect function. - SMcNeill - 10-11-2024

Am I the only person who tends to see and use file names such as the following:

Code: (Select All)
\\10.243.1.1\My Book Duo\CYOA

There is no "C:\" there for the root drive, as the root drive is a network drive and the root for it would probably be "\\10.243.1.1\".

(Though there's nothing at "\\10.243.1.1\" except for the "My Book Duo\" drive label, so for a lot of purposes, I guess it could be considered root.)

You would think that the _FILE$ command would have an easy way to check for this such as "If there's no directory above your current one, then don't list "..\" as a directory entry."  Of course, I guess that just makes too much sense to implement for the guys who make directory structures. Tongue


RE: Need a little help with updating a FileSelect function. - Pete - 10-11-2024

Yes, you're the only one, but don't worry, I'm opening up an online dating service next week. It's called OnlyFans. It's for folks who are too poor to own the rest of what goes into a real working laptop... where everyone else uses the letter drive in the file name. Tongue Big Grin 

Pete

-Enter coupon code: "Harris Sucks" for a free date.


RE: Need a little help with updating a FileSelect function. - a740g - 10-14-2024

(10-11-2024, 01:46 PM)Dav Wrote: @luke: Thank you very much!

@bplus: Thanks for testing. Yes, those are nice functions. I wanted a customized look.  Also thinking of making a clone of my old favorite filemanager from DOS a days, which listed files like this. I may go back to SHELL and temp files for that though, because I can get a lot more file info that way. I often thought it would be nice if there was a _FILEINFO function to retrieve file/dir details, things like if read only, last modified, permissions, date, etc.

- Dav
The only problem with getting file information using SHELL is that getting it to work cross-platform is a pain.

How about something like GetAttr function (_GETATTR) and FileLen function (_FILELEN)?


RE: Need a little help with updating a FileSelect function. - SMcNeill - 10-14-2024

How about a   _FileAttr (*option$*)?

_FileAttr("ReadWrite")
_FileAttr("Size")
_FileAttr("CreationDate")
_FileAttr("blahblablah")