FileChooser multi selection (and zenity platform dialog)


#1

i’ve added it cause i needed a multi file selector (the internal let you only select one at time).

it works very good, but if you don’t have zenity installed it fallback to nothing.

//==============================================================================
void FileChooser::showPlatformDialog (OwnedArray<File>& results,
                                      const String& title,
                                      const File& file,
                                      const String& filters,
                                      bool isDirectory,
                                      bool isSave,
                                      bool warnAboutOverwritingExistingFiles,
                                      bool selectMultipleFiles,
                                      FilePreviewComponent* previewComponent)
{
    String separator = "|";
    String command = "zenit --file-selection";
    String resultString;
    
    if (title != String::empty)
        command << " --title=\"" << title << "\"";

    if (file != File::nonexistent)
        command << " --filename=\"" << file.getFullPathName () << "\"";

    if (isDirectory)
        command << " --directory";

    if (isSave)
        command << " --save";

    if (selectMultipleFiles)
        command << " --multiple --separator=\"" << separator << "\"";

    command << " 2>&1";

    int status = -1;
    FILE* stream = popen ((const char*) command, "r");
    if (stream != NULL)
    {
        int bytesRead, bufferSize = 1024;
        char buffer[bufferSize + 1];
    
        while ((bytesRead = fread (buffer, 1, bufferSize, stream)) > 0)
        {
            buffer[bytesRead] = 0;
            resultString += String (buffer);
        }

        status = pclose (stream);
    }

    if (status == 0)
    {
        StringArray tokens;

        if (selectMultipleFiles)
            tokens.addTokens (resultString, separator, 0);
        else
            tokens.add (resultString);

        for (int i = 0; i < tokens.size(); i++)
            results.add (new File (tokens[i]));

        return;
    }

    //xxx ain't got one!
    jassertfalse
}

What about adding a static function (in the FileChooser maybe) to be called in order to know if the system have a native dialog capability, and if it does not, it will fall back using the juce implementation ?


#2

Anyway i also implemented multiple file selection in the non-native FileChooser. Isn’t properly implemented (i think using Array does too much copying around) but basically it works.

Here the changes:

Index: /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileListComponent.cpp
===================================================================
--- /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileListComponent.cpp (revision 1)
+++ /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileListComponent.cpp (revision 31)
@@ -43,5 +43,6 @@
 
 //==============================================================================
-FileListComponent::FileListComponent (DirectoryContentsList& listToShow)
+FileListComponent::FileListComponent (DirectoryContentsList& listToShow,
+                                      const bool selectMultipleFiles)
     : ListBox (String::empty, 0),
       DirectoryContentsDisplayComponent (listToShow)
@@ -49,4 +50,5 @@
     setModel (this);
     setRowHeight (getLookAndFeel().getFileListComponentRowHeight());
+    setMultipleSelectionEnabled (selectMultipleFiles);
     fileList.addChangeListener (this);
 }
@@ -61,4 +63,15 @@
 {
     return fileList.getFile (getSelectedRow());
+}
+
+Array<File> FileListComponent::getSelectedFiles() const
+{
+    Array<File> fileArray;
+    
+    SparseSet<int> selectedRows = getSelectedRows();
+    for (int i = 0; i < selectedRows.size (); i++)
+        fileArray.add (fileList.getFile (selectedRows[i]));
+
+    return fileArray;
 }
 
Index: /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileChooser.cpp
===================================================================
--- /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileChooser.cpp (revision 30)
+++ /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileChooser.cpp (revision 31)
@@ -51,4 +51,8 @@
       useNativeDialogBox (useNativeDialogBox_)
 {
+#if JUCE_LINUX
+    useNativeDialogBox = false;
+#endif
+
     if (! fileFilters.containsNonWhitespaceChars())
         filters = T("*");
@@ -137,5 +141,6 @@
                                                            : (isSave ? FileBrowserComponent::saveFileMode
                                                                      : FileBrowserComponent::loadFileMode),
-                                               startingFile, &wildcard, previewComponent);
+                                               startingFile, &wildcard, previewComponent,
+                                               false, false, selectMultipleFiles);
 
         FileChooserDialogBox box (title, String::empty,
@@ -145,5 +150,9 @@
 
         if (box.show())
-            results.add (new File (browserComponent.getCurrentFile()));
+        {
+            Array<File> files = browserComponent.getCurrentFiles();
+            for (int i = 0; i < files.size (); i++)
+                results.add (new File (files[i]));
+        }
     }
 
Index: /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileListComponent.h
===================================================================
--- /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileListComponent.h (revision 1)
+++ /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileListComponent.h (revision 31)
@@ -60,5 +60,6 @@
     /** Creates a listbox to show the contents of a specified directory.
     */
-    FileListComponent (DirectoryContentsList& listToShow);
+    FileListComponent (DirectoryContentsList& listToShow,
+                       const bool selectMultipleFiles = false);
 
     /** Destructor. */
@@ -71,4 +72,10 @@
     */
     const File getSelectedFile() const;
+    
+    /** Returns the files that the user has currently selected.
+
+        Returns File::nonexistent if none is selected.
+    */
+    Array<File> getSelectedFiles() const;
 
     /** Scrolls to the top of the list. */
Index: /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileBrowserComponent.cpp
===================================================================
--- /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileBrowserComponent.cpp (revision 1)
+++ /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileBrowserComponent.cpp (revision 31)
@@ -60,5 +60,6 @@
                                             FilePreviewComponent* previewComp_,
                                             const bool useTreeView,
-                                            const bool filenameTextBoxIsReadOnly)
+                                            const bool filenameTextBoxIsReadOnly,
+                                            const bool selectMultipleFiles)
    : directoriesOnlyFilter (0),
      mode (mode_),
@@ -90,5 +91,5 @@
     if (useTreeView)
     {
-        FileTreeComponent* const tree = new FileTreeComponent (*fileList);
+        FileTreeComponent* const tree = new FileTreeComponent (*fileList, selectMultipleFiles);
         addAndMakeVisible (tree);
         fileListComponent = tree;
@@ -96,5 +97,5 @@
     else
     {
-        FileListComponent* const list = new FileListComponent (*fileList);
+        FileListComponent* const list = new FileListComponent (*fileList, selectMultipleFiles);
         list->setOutlineThickness (1);
         addAndMakeVisible (list);
@@ -177,4 +178,19 @@
 {
     return currentRoot.getChildFile (filenameBox->getText());
+}
+
+Array<File> FileBrowserComponent::getCurrentFiles() const throw()
+{
+    FileListComponent* list = dynamic_cast<FileListComponent*> (fileListComponent);
+    if (list != 0)
+        return list->getSelectedFiles ();
+
+    FileTreeComponent* tree = dynamic_cast<FileTreeComponent*> (fileListComponent);
+    if (tree != 0)
+        return tree->getSelectedFiles ();
+
+    Array<File> files;
+    files.add (currentRoot.getChildFile (filenameBox->getText()));
+    return files;
 }
 
Index: /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileBrowserComponent.h
===================================================================
--- /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileBrowserComponent.h (revision 1)
+++ /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileBrowserComponent.h (revision 31)
@@ -100,5 +100,6 @@
                           FilePreviewComponent* previewComp,
                           const bool useTreeView = false,
-                          const bool filenameTextBoxIsReadOnly = false);
+                          const bool filenameTextBoxIsReadOnly = false,
+                          const bool selectMultipleFiles = false);
 
     /** Destructor. */
@@ -109,4 +110,9 @@
     */
     const File getCurrentFile() const throw();
+
+    /**
+    */
+    Array<File> getCurrentFiles() const throw();
+
 
     /** Returns true if the current file is usable.
Index: /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileTreeComponent.cpp
===================================================================
--- /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileTreeComponent.cpp (revision 1)
+++ /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileTreeComponent.cpp (revision 31)
@@ -235,5 +235,6 @@
 
 //==============================================================================
-FileTreeComponent::FileTreeComponent (DirectoryContentsList& listToShow)
+FileTreeComponent::FileTreeComponent (DirectoryContentsList& listToShow,
+                                      const bool selectMultipleFiles)
     : DirectoryContentsDisplayComponent (listToShow)
 {
@@ -243,4 +244,5 @@
 
     root->setSubContentsList (&listToShow);
+    setMultiSelectEnabled (selectMultipleFiles);
     setRootItemVisible (false);
     setRootItem (root);
@@ -260,4 +262,19 @@
 }
 
+Array<File> FileTreeComponent::getSelectedFiles() const throw()
+{
+    Array<File> files;
+    
+    for (int i = 0; i < getNumSelectedItems (); i++)
+    {
+        const FileListTreeItem* const item = dynamic_cast <const FileListTreeItem*> (getSelectedItem (i));
+
+        if (item != 0)
+            files.add (item->file);
+    }
+
+    return files;
+}
+
 const File FileTreeComponent::getSelectedFile (const int index) const throw()
 {
Index: /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileTreeComponent.h
===================================================================
--- /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileTreeComponent.h (revision 1)
+++ /trunk/juce/src/juce_appframework/gui/components/filebrowser/juce_FileTreeComponent.h (revision 31)
@@ -56,5 +56,6 @@
     /** Creates a listbox to show the contents of a specified directory.
     */
-    FileTreeComponent (DirectoryContentsList& listToShow);
+    FileTreeComponent (DirectoryContentsList& listToShow,
+                       const bool selectMultipleFiles = false);
 
     /** Destructor. */
@@ -65,4 +66,8 @@
     */
     int getNumSelectedFiles() const throw()         { return TreeView::getNumSelectedItems(); }
+
+    /** Returns the selected files in the tree.
+    */
+    Array<File> getSelectedFiles() const throw();
 
     /** Returns one of the files that the user has currently selected.