C/C++ Programmierung: Durchsuchen eines Programmordners und Erstellen einer Ordner-Übersicht

Im Artikel Durchsuchen eines Programmordners und Auswählen einer Datei haben wir uns damit befasst, wie sich zur Laufzeit eines Programms eine beliebige Datei innerhalb eines Ordners auswählen lässt, auf die dann zu einem späteren Zeitpunkt zugegriffen (öffnen und auslesen) werden kann.
In unserem konkreten Fall handelte es sich hierbei um Animationsposen sowie um 3D-Modelle samt Animations-Skelett. Die beiden hierfür verantwortlichen Funktionen
Select_NewModelAndSkeleton() sowie Select_NewAnimationPose() konnten uns jedoch zum damaligen Zeitpunkt keine Übersicht über die in den Ordnern gespeicherten Dateien liefern.

Um solch eine Ordner-Übersicht zu erstellen, könnte man nun alle Dateinamen auslesen und jeden Namen in einem separaten char-Array speichern. Dieses Vorgehen birgt jedoch zwei Nachteile. Zum einen müsste man zuvor die Anzahl der Dateien ermitteln und zum anderen würde bei sehr umfangreichen Ordnern das Auslesen und Kopieren aller Namen einiges an Rechenzeit kosten.
Zur Vermeidung dieser Probleme werden wir daher nur eine vom Benutzer festgelegte Anzahl von Dateinamen (g_FolderOverview_NumFilenamesDisplayedMax) im Rahmen der Übersicht berücksichtigen.

Für die Verwaltung und Aktualisierung der Übersichts-Daten erzeugen wir zunächst zwei Instanzen der CFileInfoOverview-Klasse und legen ferner die maximale Anzahl der in der jeweiligen Ordner-Übersicht anzuzeigenden Dateinamen fest:

CFileInfoOverview g_ModelFileOverview;
CFileInfoOverview g_AnimationPoseFileOverview;

g_ModelFileOverview.Set_NumberOfFiles(
                    g_FolderOverview_NumFilenamesDisplayedMax);

g_AnimationPoseFileOverview.Set_NumberOfFiles(
                            g_FolderOverview_NumFilenamesDisplayedMax);

Damit beim Aufruf der Funktionen Select_NewModelAndSkeleton() bzw. Select_NewAnimationPose() eine aktualisierte Übersicht erstellt wird, sind einige Modifikationen am Source Code notwendig. Da beide Funktionen vom Aufbau her nach wie vor völlig identisch sind, genügt es, wenn wir unser Augenmerk wiederum nur auf eine der Funktionen richten:

void Select_NewAnimationPose(char* pDirectory)
{
    long   hFile;               // wichtig für
    struct _finddata_t c_file;  // das Datei-Management

    char strBuffer[200];

    char TempFileName[MaxLength];
    char TempFileNameForOverview[MaxLength];

    long SavedNewFileNr = 0;
    long WritePos = 0;
    long WritePosForOverview = 0;

    sprintf(strBuffer, "%s*", pDirectory);

    if((hFile = _findfirst(strBuffer, &c_file )) != -1L)
    {
        // Zurücksetzen der Übersicht:
        g_AnimationPoseFileOverview.Reset();

        // Berechnen, welcher Dateiname zuerst in die Übersicht
        // aufgenommen wird. Das Ergebnis ist abhängig von der vom
        // Benutzer aktuell ausgewählen Datei:
        g_AnimationPoseFileOverview.Calculate_IDOfFirstFileInOverview(
                                    g_NewAnimationPoseFileNr);

        while(_findnext( hFile, &c_file ) == 0)
        {
            // Erstellen der aktuellen Übersicht:

           
if(g_AnimationPoseFileOverview.NumUsedElements <
               g_AnimationPoseFileOverview.NumOfFiles)
            {
            if(c_file.name[0] != '.')
            {
                ZeroMemory(TempFileNameForOverview, MaxLength);

                for(WritePosForOverview = 0;
                    WritePosForOverview < MaxLength; WritePosForOverview++)
                {
                    if(c_file.name[WritePosForOverview] == '.')
                        break;

                    TempFileNameForOverview[WritePosForOverview] =
                    c_file.name[WritePosForOverview];
                }

                g_AnimationPoseFileOverview.Set_FileInfo(
                TempFileNameForOverview, SavedNewFileNr);
            }}

            // Die vom Benutzer ausgewählte Datei ist gefunden:
            if(SavedNewFileNr == g_NewAnimationPoseFileNr)
            {
                if(c_file.name[0] == '.')
                    continue;

                ZeroMemory(TempFileName, MaxLength);

                for(WritePos = 0; WritePos < MaxLength; WritePos++)
                {
                    if(c_file.name[WritePos] == '.')
                        break;

                    TempFileName[WritePos] = c_file.name[WritePos];
                }
            }

            SavedNewFileNr++;
        }

        _findclose( hFile );

        // Restlicher Source Code ist unverändert geblieben.

    }
}

Anzeigen lassen sich die einzelnen Dateinamen aus der neu erstellten Übersicht dann beispielsweise wie folgt:

pFont2D->Output_Text(0.7f, 0.9f, 0.04f, &D3DXVECTOR3(1.0f, 0.0f, 0.0f),
                     "Animationsposen:");

for(long i = 0; i < g_AnimationPoseFileOverview.NumOfFiles; i++)
{
    if(g_AnimationPoseFileOverview.FileInfo[i].inUse == true)
    {
    sprintf(text, "%s", g_AnimationPoseFileOverview.FileInfo[i].FileName);

    if(g_AnimationPoseFileOverview.FileInfo[i].FileID ==
       g_NewAnimationPoseFileNr)
        pFont2D->Output_Text(0.7f, 0.825f-0.05f*(float)i, 0.04f,
                             &D3DXVECTOR3(1.0f, 0.0f, 0.0f), text);
    else
        pFont2D->Output_Text(0.7f, 0.825f-0.05f*(float)i, 0.04f,
                             &D3DXVECTOR3(0.0f, 1.0f, 0.0f), text);
    }
}

Gespeichert werden die Dateinamen in separaten Instanzen der CFileInfo-Klasse (FileInfo), die ihrerseits von der CFileInfoOverview-Klasse verwaltet werden. Da beim Aufstellen der Übersicht zunächst nicht abzusehen ist, ob wirkliche alle CFileInfo-Instanzen benötigt werden, lassen sich die tatsächlich verwendeten Instanzen zu einem späteren Zeitpunkt – beispielsweise beim Anzeigen der einzelnen Dateinamen – anhand des inUse-Flags identifizieren.

class CFileInfo
{
public:

    char FileName[MaxLength];
    long FileID;
    bool inUse;

    CFileInfo()
    {
        FileID   = -1;
        inUse = false;

        ZeroMemory(FileName, MaxLength);
    }

    ~CFileInfo()
    {};

    void Reset(void)
    {
        FileID   = -1;
        inUse = false;

        ZeroMemory(FileName, MaxLength);
    }
};

Kommen wir nun zum eigentlichen Arbeitspferd, der CFileInfoOverview-Klasse. Bevor wir uns mit dem Source Code befassen, verschaffen wir uns zunächst einen Überblick über die einzelnen Klassen-Methoden, die uns beim Erstellen und Aktualisieren der Verzeichnis-Übersicht behilflich sind:

CFileInfoOverview(long numOfFiles): Zusätzlicher Konstruktor, mit dessen Hilfe sich die maximale Anzahl der Dateinamen, die in der Ordner-Übersicht angezeigt werden sollen, bei der Initialisierung festlegen lässt.

Set_NumberOfFiles(): Legt die maximale Anzahl der Dateinamen fest, die in der Ordner-Übersicht angezeigt werden sollen.

Reset(): Setzt die aktuelle Übersicht zurück. Muss vor dem Erstellen einer neuen Übersicht aufgerufen werden.

Set_IDOfFirstFileInOverview(): Legt die Nummer der ersten Datei fest (IDOfFirstFileInOverview), deren Name in die Übersicht aufgenommen wird.

Calculate_IDOfFirstFileInOverview(): Berechnet die Nummer der ersten Datei (IDOfFirstFileInOverview), deren Name in die Übersicht aufgenommen wird, anhand der als Parameter übergebenen Dateinummer.

Set_FileInfo(): Fügt einen neuen Dateinamen in die Übersicht ein, falls die zugehörige Dateinummer größer oder gleich IDOfFirstFileInOverview ist.

Hier nun der zugehörige Source Code:

class CFileInfoOverview
{
public:

    long IDOfFirstFileInOverview;

    long NumOfFiles;
    long NumUsedElements;
    CFileInfo* FileInfo;

    CFileInfoOverview()
    {
        NumOfFiles = 0;
        FileInfo = NULL;
        NumUsedElements = 0;
    }

    CFileInfoOverview(long numOfFiles)
    {
        NumOfFiles = numOfFiles;

        if(NumOfFiles < 3)
            NumOfFiles = 3;

        FileInfo = new CFileInfo[NumOfFiles];

        IDOfFirstFileInOverview = 0;
        NumUsedElements = 0;
    }

    ~CFileInfoOverview()
    {
        SAFE_DELETE_ARRAY(FileInfo)
    }

    void Set_NumberOfFiles(long numOfFiles)
    {
        SAFE_DELETE_ARRAY(FileInfo)

        NumOfFiles = numOfFiles;

        if(NumOfFiles < 3)
            NumOfFiles = 3;

        FileInfo = new CFileInfo[NumOfFiles];

        IDOfFirstFileInOverview = 0;
        NumUsedElements = 0;
    }

    void Reset(void)
    {
        if(FileInfo == NULL)
            return;

        NumUsedElements = 0;

        for(long i = 0; i < NumOfFiles; i++)
            FileInfo[i].Reset();
    }

    void Calculate_IDOfFirstFileInOverview(long idOfSelectedFile)
    {
        if(idOfSelectedFile < 0)
            idOfSelectedFile = 0;

        IDOfFirstFileInOverview = idOfSelectedFile - NumOfFiles/2;

        if(IDOfFirstFileInOverview < 0)
            IDOfFirstFileInOverview = 0;
    }

    void Set_IDOfFirstFileInOverview(long id)
    {
        IDOfFirstFileInOverview = id;
    }

    void Set_FileInfo(char* pFileName, long FileNr)
    {
        if(NumUsedElements >= NumOfFiles)
            return;

        if(FileNr < IDOfFirstFileInOverview)
            return;

        FileInfo[NumUsedElements].FileID = FileNr;
        FileInfo[NumUsedElements].inUse = true;
        strcpy(FileInfo[NumUsedElements].FileName, pFileName);

        NumUsedElements++;
    }
};