Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Native Development

Reply
Developer
borceg
Posts: 671
Registered: ‎03-21-2012
My Device: BlackBerry PlayBook 16GB
Accepted Solution

Using native filebrowse and filesave dialog in Cascades

Within my app I need file open and file save dialog when specific button is clicked. I looked over Cascades docs, but I found only native options. Tried the provided native alert dialog, changed it to filebrowse dialog type. It displays fine on simulator, but can't get any output file that will be used in Qt code.

I pass these parameters 

int *num =NULL;

char **file[1024];
dialog_update(alert_dialog);

to dialog_event_get_filebrowse_filepaths(event,file,num), but it always returns BPS_FAILURE.

 

Also, <action>access_shared</action> is present in bar-descriptor

 

Here is my code:

 

//slot function
void App::fileOpen(){
	//===========================
		dialog_instance_t alert_dialog = 0;

		bps_initialize();

		dialog_request_events(0);    //0 indicates that all events are requested

		if (dialog_create_filebrowse(&alert_dialog) != BPS_SUCCESS) {
		            fprintf(stderr, "Failed to create alert dialog.");
		            return ;
		    }
		    const char* extensions[] = {"*.*","*.jpg","*.jpeg","*.mp3","*.wav","*.mp4","*.txt","*.doc","*.pdf"};
		    int items = 9;
		    if(dialog_set_filebrowse_filter(alert_dialog, extensions,items) != BPS_SUCCESS){
		    	fprintf(stderr, "Failed to set alert dialog message text.");
		    	        dialog_destroy(alert_dialog);
		    	       alert_dialog = 0;
		    	       return ;
		    }
		   if( dialog_set_filebrowse_multiselect(alert_dialog,FALSE)!=BPS_SUCCESS){
			   fprintf(stderr, "Failed to set alert dialog message text.");
			       	        dialog_destroy(alert_dialog);
			       	       alert_dialog = 0;
			       	       return ;
		   }





		if (dialog_show(alert_dialog) != BPS_SUCCESS) {
		    fprintf(stderr, "Failed to show alert dialog.");
		    dialog_destroy(alert_dialog);
		    alert_dialog = 0;
		    return ;
		}

		int shutdown =0;
		while (!shutdown) {
		    bps_event_t *event = NULL;
		    bps_get_event(&event, -1);    // -1 means that the function waits
		                                  // for an event before returning

		    if (event) {
		        if (bps_event_get_domain(event) == dialog_get_domain()) {

		            int selectedIndex =
		                dialog_event_get_selected_index(event);
		            const char* label =
		                dialog_event_get_selected_label(event);
		            const char* context =
		                dialog_event_get_selected_context(event);

		            char **fileArray[]={};
		            int *numFiles = NULL;
		           //
                          if(selectedIndex == 0){
	   shutdown = 1;//user press the cancel button on dialog; close the dialog
   }
   else if(selectedIndex == 1){
	   if(dialog_event_get_filebrowse_filepaths(event,file,num)!=BPS_SUCCESS){
	   fprintf(stderr,"File open fail");
   }
   else{
//debug purposes
	   fprintf(stderr,"File array: %d/n",sizeof(file)*1024);
	   	   fprintf(stderr,"Num files: %n",num);
	   	   //fprintf(stderr,"Files int: %d",files);
   }

   }
		        }
		    }
		}

		if (alert_dialog) {
		    dialog_destroy(alert_dialog);
		}
		//===========================
}

 

Native subforums lack any usefull info about this topic. Any help is greatly appreciated 

Please use plain text.
Developer
borceg
Posts: 671
Registered: ‎03-21-2012
My Device: BlackBerry PlayBook 16GB

Re: Using native filebrowse and filesave dialog in Cascades

Nobody ?

Please use plain text.
BlackBerry Development Advisor (Retired)
selom
Posts: 60
Registered: ‎05-10-2012
My Device: Blackberry 10 Alpha

Re: Using native filebrowse and filesave dialog in Cascades

[ Edited ]

Hello,

 

I will supply you with some code later today that shows how to use the native filebrowse dialog directly from QML.

 

Cheers

Selom

Please use plain text.
BlackBerry Development Advisor (Retired)
selom
Posts: 60
Registered: ‎05-10-2012
My Device: Blackberry 10 Alpha

Re: Using native filebrowse and filesave dialog in Cascades

Hello again, here is the sample as promised.

 

To use the native filebrowse dialog, the native code must run in its own thread to prevent the UI in Cascades from blocking. This is achieved by encapsulating all the dialog code in a class derived from QThread.  The class I wrote is named FileBrowseDialog

 

FileBrowseDialog.hpp

#ifndef FILEBROWSEDIALOG_HPP_
#define FILEBROWSEDIALOG_HPP_

#include <QThread>
#include <QVariant>
#include <bps/dialog.h>

/*
 * The file browse dialog displays a dialog to browse and select
 * files from shared folders on the system.
 */
class FileBrowseDialog : public QThread
{
    Q_OBJECT

    /*
     * QML property to allow multiple selection
     */
    Q_PROPERTY(bool multiselect READ getMultiSelect WRITE setMultiSelect)

    /*
     * QML property to read the selected filenames
     */
    Q_PROPERTY(QVariant filepaths READ getFilePaths)

    /*
     * QML property to set or get the file filters. This is an
     * list array variant.
     */
    Q_PROPERTY(QVariant filters READ getFilters WRITE setFilters)
public:
    /*
     * Ctor and Dtor
     */
    FileBrowseDialog(QObject* parent = 0);
    virtual ~FileBrowseDialog();

    /*
     * Exposed to QML to start the run loop which creates and displays the dialog.
     * The dialog is shown until a button is clicked.
     */
    Q_INVOKABLE void show();

public:
    /*
     * Getter for the selected filenames QML property
     */
    QVariant getFilePaths() const;

    /*
     * Setter and Getter for the filters QML property
     */
    QVariant getFilters() const;
    void setFilters(QVariant const& value);

    /*
     * Getter and Setter for the multiselect QML property
     */
    bool getMultiSelect() const;
    void setMultiSelect(bool value);

signals:
    /*
     * Signal emitted when the OK button has been clicked on the browse dialog
     * The OK button is not enabled unless a file is selected
     */
    void selectionCompleted();

    /*
     * Signal emitted when the cancel button has been clicked on the browse dialog
     */
    void selectionCancelled();

protected:
    /*
     * Implements the run loop. Dialog stays open until a button is clicked.
     */
    virtual void run();

protected:
    dialog_instance_t m_dialog;
    bool m_multiSelect;
    QVariantList m_filePaths;
    QVariantList m_filters;
};

#endif /* FILEBROWSEDIALOG_HPP_ */

 

FileBrowseDialog.cpp

#include "FileBrowseDialog.hpp"
#include <bps/bps.h>
#include <QDebug>

FileBrowseDialog::FileBrowseDialog(QObject* parent)
    : QThread(parent)
    , m_multiSelect(false)
{
    m_filters.push_back(QString("*.*"));
}

FileBrowseDialog::~FileBrowseDialog()
{
}

void FileBrowseDialog::show()
{
    if (!isRunning())
    {
    	m_filePaths.clear();
        start();
    }
}

QVariant FileBrowseDialog::getFilePaths() const
{
    return m_filePaths;
}

bool FileBrowseDialog::getMultiSelect() const
{
    return m_multiSelect;
}

void FileBrowseDialog::setMultiSelect(bool value)
{
    m_multiSelect = value;
}

QVariant FileBrowseDialog::getFilters() const
{
    return m_filters;
}

void FileBrowseDialog::setFilters(QVariant const& value)
{
    m_filters = value.toList();
    qDebug() << "filter count: " << m_filters.count();
}

void FileBrowseDialog::run()
{
    bps_initialize();

    //request all dialog events
    dialog_request_events(0);
    if (dialog_create_filebrowse(&m_dialog) != BPS_SUCCESS)
    {
        qDebug() << "Failed to create file browse dialog.";
        emit selectionCancelled();
        return;
    }

    //set the selection filters
    if (m_filters.count() > 0)
    {
        char** ext = (char**)new char[m_filters.count()*sizeof(char*)];
        int i = 0;
        for (QVariantList::iterator it = m_filters.begin(); it != m_filters.end(); ++it, ++i)
        {
            QString filter = it->toString();
            if (!filter.trimmed().isEmpty())
            {
                int length = (filter.length() + 1) * sizeof(char);
                ext[i] = new char[length];
                strncpy(ext[i], filter.toAscii(), length);
            }
        }
        if (dialog_set_filebrowse_filter(m_dialog, (const char**)ext, m_filters.count()) != BPS_SUCCESS)
        {
            qDebug() << "unable to set file browse dialog extensions";
        }
        for (i = 0; i < m_filters.count(); i++)
        {
            delete ext[i];
        }
        delete ext;
    }

    if (dialog_show(m_dialog) != BPS_SUCCESS)
    {
        qDebug() << "Failed to show file browse dialog.";
        dialog_destroy(m_dialog);
        m_dialog = 0;
        emit selectionCancelled();
        return;
    }

    bool shutdown = false;
    while (!shutdown)
    {
        bps_event_t* event = NULL;
        bps_get_event(&event, -1);    // -1 means that the function waits
        // for an event before returning

        if (event)
        {
            if (bps_event_get_domain(event) == dialog_get_domain())
            {
                //0=ok, 1=cancel
                int selectedIndex = dialog_event_get_selected_index(event);

                if (selectedIndex == 1)
                {
                    int count;
                    char** filepaths;
                    if (BPS_SUCCESS == dialog_event_get_filebrowse_filepaths(event, &filepaths, &count))
                    {
                        for (int i = 0; i < count; i++)
                        {
                            qDebug() << "selected file: " << filepaths[i];
                            m_filePaths.push_back(QString(filepaths[i]));
                        }
                        bps_free(filepaths);
                    }
                    emit selectionCompleted();
                }
                else
                {
                    emit selectionCancelled();
                }

                qDebug() << "Got file browse dialog click";
                shutdown = true;
            }
        }
    }

    if (m_dialog)
    {
        dialog_destroy(m_dialog);
    }
}

 

This class derives from QObject (through QThread) which means it can be used by QML when it exposes properties and signals. The FileBrowseDialog class has 3 properties

 

- multiselect: a boolean flag indicating whether single or multiselection is allowed

- filepaths: a read only value that returns the list of files selected

- filters: a read/write value where you can specify one or more file filters (e.g "*.doc","*.jpg") etc.

 

 

The next part is how you invoke the FileBrowseDialog through the QML. To do this, we need to let the QML page know about the FileBrowseDialog. This is done in the App class through the qmlregistertype code.

 

App.cpp

#include <bb/cascades/Application>
#include <bb/cascades/QmlDocument>
#include <bb/cascades/AbstractPane>

#include "app.hpp"
#include "FileBrowseDialog.hpp"

using namespace bb::cascades;

App::App()
{
    qmlRegisterType<FileBrowseDialog>("Dialog.FileBrowse", 1, 0, "FileBrowseDialog");
    QmlDocument *qml = QmlDocument::create("main.qml");
    qml->setContextProperty("cs", this);

    AbstractPane *root = qml->createRootNode<AbstractPane>();
    Application::setScene(root);
}

 

 

Now the QML is ready to be able to use the FileBrowseDialog. The sample below is a complete qml page that has a button and a label. When the button is clicked the FileBrowseDialog is opened, and any files selected will be displayed in the label.

 

Main.qml

import bb.cascades 1.0
import Dialog.FileBrowse 1.0

Page {
    content: Container {
        Label { id: filebrowseDialogLabel }
        Button {
            text : "File Browse Dialog"
            onClicked: {
                filebrowseDialog.show();
            }
        }
        attachedObjects: [
            FileBrowseDialog {
                id: filebrowseDialog
                multiselect : true
                filters : ["*.doc","*.jpg","*.txt"]
                onSelectionCompleted: {
                    if(filebrowseDialog.filepaths.length>0)
                        filebrowseDialogLabel.text = filebrowseDialog.filepaths[0];
                    else
                        filebrowseDialogLabel.text = "no file selected";
                }
                onSelectionCancelled: {
                    filebrowseDialogLabel.text = "file browse dialog was cancelled";
                }
            }
        ]
    }
}

 

 

And that's pretty much all it takes to invoke the native file browsing dialog in from Cascades. Please note the file save would follow a similar pattern, but I found this dialog was not particularly useful as it only displays a simple dialogbox with a text entry of a filename.

 

Cheers

Selom

Please use plain text.
Developer
borceg
Posts: 671
Registered: ‎03-21-2012
My Device: BlackBerry PlayBook 16GB

Re: Using native filebrowse and filesave dialog in Cascades

That was fast, really fast. Should go to Cascades samples on github. Thank man, it's clearer now.
Please use plain text.