qtpathqfiledialogqdir

Qt: How to find the nearest existing ancestor of a path, which itself may or may not exist


Motivating example:

In a previous session, the application has stored some path selected by the user. In the meantime that path may have been deleted, moved, renamed or the drive unmounted. The application would now like to let the user browse for a path via a QFileDialog and for the user's convenience, the previous path is passed as the starting directory of the file dialog, as presumably the new path is likely to be near the old path. Unfortunately, if QFileDialog is given a starting path that does not exist, it defaults to the current working directory, which is very unlikely to be helpful to the user as it is typically the installation directory of the application.

So we would like to preprocess the old path to point to a directory that actually exists before passing it to QFileDialog. If the old path doesn't exist, we'd like to replace it with the nearest directory that does.

So how does one take a file path (which may or may not exist) and search "up" that path until one finds something that actually exists in the filesystem?


Solution

  • These are the two approaches I've come up with so far, but suggestions for improvement would be very much appreciated.

    Searching upward until a path exists:

    QString GetNearestExistingAncestorOfPath(const QString & path)
    {
        if(QFileInfo::exists(path)) return path;
    
        QDir dir(path);
        if(!dir.makeAbsolute()) return {};
        do
        {
            dir.setPath(QDir::cleanPath(dir.filePath(QStringLiteral(".."))));
        }
        while(!dir.exists() && !dir.isRoot());
    
        return dir.exists() ? dir.path() : QString{};
    }
    

    Searching downward until a path doesn't exist:

    QString GetNearestExistingAncestorOfPath(const QString & path)
    {
        if(QFileInfo::exists(path)) return path;
    
        auto segments = QDir::cleanPath(path).split('/');
        QDir dir(segments.takeFirst() + '/');
        if(!dir.exists()) return {};
        for(const auto & segment : qAsConst(segments))
        {
            if(!dir.cd(segment)) break;
        }
        return dir.path();
    }