Create a playlist table displaying files from directory

I am fairly new to C++ and JUCE and frankly am finding C++ to be one of the more difficult languages I’ve practiced. I am creating a simple DJ deck type application using JUCE and Visual Studio on Windows. I have been researching how to implement a playlist that gets and displays audio files, and file meta data from a directory. So far I have created a table to display the tracks but am having trouble understanding how to display the directory contents. I have seen in this forum thread that I can use DirectoryContentsList to do this however I have tried to implement it but only receive the error “no defult constructor exists for class ‘juce::DisplayContentsList’”. Can anyone tell me how to implement this in more detail and help me by providing any further advice for displaying the audio files and their meta data, adding more files and adding a search feature?

ee my code so far below:

PlaylistComponent.h

#pragma once
#include <vector>
#include <string>
#include <JuceHeader.h>
#include "DeckGUI.h"
using namespace juce;


class PlaylistComponent  : public Component,
                       public TableListBoxModel,
                       public DirectoryContentsList,
                       public Button::Listener
{
public:
    PlaylistComponent();
    ~PlaylistComponent() override;

    void paint (Graphics&) override;
    void resized() override;
    int getNumRows () override;
    void paintRowBackground (Graphics &, int rowNumber,
                         int width, int height, 
                         bool rowIsSelected) override;
    void paintCell (Graphics &, int rowNumber,
                int columnId, int width,
                int height, bool rowIsSelected) override;
    Component* refreshComponentForCell(int rowNumber, 
                                   int columnId,
                                   bool isRowSelected, 
                                   Component* existingComponentToUpdate) override;
    void buttonClicked(Button* button) override;

private:

    TableListBox tableComponent;
    DirectoryContentsList trackPlaylist;
    std::vector<std::string> trackTitles;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PlaylistComponent)
};

PlaylistComponent.cpp

#include <JuceHeader.h>
#include "PlaylistComponent.h"

PlaylistComponent::PlaylistComponent()
{
    // In your constructor, you should add any child components, and
    // initialise any special settings that your component needs.
    trackTitles.push_back("Track 1");
    trackTitles.push_back("Track 2");
    trackTitles.push_back("Track 3");

    tableComponent.getHeader().addColumn("Track Title", 1, 400);
    tableComponent.getHeader().addColumn(" ", 2, 200);

    tableComponent.setModel(this);

    addAndMakeVisible(tableComponent);

}

PlaylistComponent::~PlaylistComponent()
{
}

void PlaylistComponent::paint (juce::Graphics& g)
{
    /* This demo code just fills the component's background and
       draws some placeholder text to get you started.

       You should replace everything in this method with your own
       drawing code..
    */

    g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));   // clear the background

    g.setColour (juce::Colours::grey);
    g.drawRect (getLocalBounds(), 1);   // draw an outline around the component

    g.setColour (juce::Colours::white);
    g.setFont (14.0f);
    g.drawText ("PlaylistComponent", getLocalBounds(),
                juce::Justification::centred, true);   // draw some placeholder text
}

void PlaylistComponent::resized()
{
    // This method is where you should set the bounds of any child
    // components that your component contains..
    tableComponent.setBounds(0, 0, getWidth(), getHeight());
}

int PlaylistComponent::getNumRows()
{
    return trackTitles.size();
}

void PlaylistComponent::paintRowBackground(Graphics & g, int rowNumber, int width, int height, bool rowIsSelected)
{
    if (rowIsSelected)
    {
        g.fillAll(Colours::turquoise);
    }
    else {
        g.fillAll(Colours::darkgrey);
    }
}

void PlaylistComponent::paintCell(Graphics & g, int rowNumber, int columnId, int width, int height, bool rowIsSelected)
{
    g.drawText(trackTitles[rowNumber],
               2, 0,
               width - 4, height,
               Justification::centredLeft, true);
}

Component* PlaylistComponent::refreshComponentForCell(int rowNumber, int columnId, bool isRowSelected, Component* existingComponentToUpdate)
{
    if (columnId == 2)
    {
        if (existingComponentToUpdate == nullptr)
        {
            TextButton* btn = new TextButton{ "Play" };
            String id{ std::to_string(rowNumber) };
            btn->setComponentID(id);
            btn->addListener(this);
            existingComponentToUpdate = btn;
        }
    }
    return existingComponentToUpdate;
}

void PlaylistComponent::buttonClicked(Button* button)
{
    int id = std::stoi(button->getComponentID().toStdString());
    DBG("PlaylistComponent::buttonClicked " << trackTitles[id]);
}

As you can see from the documentation for JUCE: DirectoryContentsList Class Reference, during construction it requires a pointer to a JUCE: FileFilter Class Reference and a reference to a JUCE: TimeSliceThread Class Reference and there is no constructor which either takes no parameters or has defaults specified, thus has no default constructor and hence the error you see at DirectoryContentsList trackPlaylist;

I think you’re going to need a bit more on C++ fundamentals before being able to progress much further.

I’m afraid I don’t have any recommendations for books or online resources to help you in this regard, but I’m sure others will chime in.

1 Like

Can you recommend another way to populate a table with files from a directory?

tbh that class is going to do a lot of things for you and will be much easier than implementing it all for yourself from scratch using a TableListBox and TableListBoxModel combo.

Unfortunately it doesn’t seem that there is a nice demo already out there to show how to implement it (not that I could find with Le Goog anyway).

Also sadly I don’t have time to knock up a demonstration for you, maybe someone else would be kind enough, but I need to get back to work and off the forum! :joy:

1 Like

You can get a list of all files in a particular directory using the File class, something like:

Array<File> dirFiles;
File dirToSearch = File::getCurrentWorkingDirectory().getChildFile("/directory/to/search");
dirFiles = dirToSearch.findChildFiles (File::TypesOfFileToFind::findFiles, false, "*.wav");

Once you have them you can populate a listbox. This is much easier than using the DirectoryContentsDisplayComponent.

2 Likes

If you choose this method, be aware that executing this code on the message thread could cause your app to stall on large directories, as well as directories that are on a slow network connection. So you may want to put this process into it’s own thread. :slight_smile:

1 Like

I understand, thanks for your help!

Very helpful thank you very much!