QNX: Adding native file dialog support for Blackberry

This patch enables use of native file dialogs in the application
modal case.

The native file open dialog is reasonable but the native file save
dialog could do with some improvements to make it more usable.
Perhaps providing our own "native" dialog UI would be an option
once the widget style is finished.

Change-Id: If5fb7cf73d27e52db7bfa6d97d8f8fb7912960bb
Reviewed-by: Nicolas Arnaud-Cormos <nicolas@kdab.com>
bb10
Sean Harmer 2012-04-25 13:16:24 +01:00 committed by Qt by Nokia
parent c4fbe872be
commit 48768ab433
9 changed files with 487 additions and 6 deletions

View File

@ -22,6 +22,7 @@ CONFIG(blackberry) {
#DEFINES += QQNXBPSEVENTFILTER_DEBUG
#DEFINES += QQNXBUFFER_DEBUG
#DEFINES += QQNXCLIPBOARD_DEBUG
#DEFINES += QQNXFILEDIALOGHELPER_DEBUG
#DEFINES += QQNXGLBACKINGSTORE_DEBUG
#DEFINES += QQNXGLCONTEXT_DEBUG
#DEFINES += QQNXINPUTCONTEXT_DEBUG
@ -88,14 +89,16 @@ CONFIG(blackberry) {
qqnxbpseventfilter.cpp \
qqnxvirtualkeyboardbps.cpp \
qqnxtheme.cpp \
qqnxsystemsettings.cpp
qqnxsystemsettings.cpp \
qqnxfiledialoghelper.cpp
HEADERS += qqnxnavigatorbps.h \
qqnxeventdispatcher_blackberry.h \
qqnxbpseventfilter.h \
qqnxvirtualkeyboardbps.h \
qqnxtheme.h \
qqnxsystemsettings.h
qqnxsystemsettings.h \
qqnxfiledialoghelper.h
LIBS += -lbps
}

View File

@ -41,6 +41,7 @@
#include "qqnxbpseventfilter.h"
#include "qqnxnavigatoreventhandler.h"
#include "qqnxfiledialoghelper.h"
#include "qqnxscreen.h"
#include "qqnxscreeneventhandler.h"
#include "qqnxvirtualkeyboardbps.h"
@ -110,6 +111,22 @@ void QQnxBpsEventFilter::unregisterForScreenEvents(QQnxScreen *screen)
qWarning("QQNX: failed to unregister for screen events on screen %p", screen->nativeContext());
}
void QQnxBpsEventFilter::registerForDialogEvents(QQnxFileDialogHelper *dialog)
{
if (dialog_request_events(0) != BPS_SUCCESS)
qWarning("QQNX: failed to register for dialog events");
dialog_instance_t nativeDialog = dialog->nativeDialog();
if (!m_dialogMapper.contains(nativeDialog))
m_dialogMapper.insert(nativeDialog, dialog);
}
void QQnxBpsEventFilter::unregisterForDialogEvents(QQnxFileDialogHelper *dialog)
{
int count = m_dialogMapper.remove(dialog->nativeDialog());
if (count == 0)
qWarning("QQNX: attempting to unregister dialog that was not registered");
}
bool QQnxBpsEventFilter::dispatcherEventFilter(void *message)
{
qBpsEventFilterDebug() << Q_FUNC_INFO;
@ -131,6 +148,13 @@ bool QQnxBpsEventFilter::bpsEventFilter(bps_event_t *event)
return m_screenEventHandler->handleEvent(screenEvent);
}
if (eventDomain == dialog_get_domain()) {
dialog_instance_t nativeDialog = dialog_event_get_dialog_instance(event);
QQnxFileDialogHelper *dialog = m_dialogMapper.value(nativeDialog, 0);
if (dialog)
return dialog->handleEvent(event);
}
if (eventDomain == navigator_get_domain())
return handleNavigatorEvent(event);

View File

@ -43,6 +43,9 @@
#define QQNXBPSEVENTFILTER_H
#include <QObject>
#include <QHash>
#include <bps/dialog.h>
struct bps_event_t;
@ -50,6 +53,7 @@ QT_BEGIN_NAMESPACE
class QAbstractEventDispatcher;
class QQnxNavigatorEventHandler;
class QQnxFileDialogHelper;
class QQnxScreen;
class QQnxScreenEventHandler;
class QQnxVirtualKeyboardBps;
@ -68,6 +72,9 @@ public:
void registerForScreenEvents(QQnxScreen *screen);
void unregisterForScreenEvents(QQnxScreen *screen);
void registerForDialogEvents(QQnxFileDialogHelper *dialog);
void unregisterForDialogEvents(QQnxFileDialogHelper *dialog);
private:
static bool dispatcherEventFilter(void *message);
bool bpsEventFilter(bps_event_t *event);
@ -78,6 +85,7 @@ private:
QQnxNavigatorEventHandler *m_navigatorEventHandler;
QQnxScreenEventHandler *m_screenEventHandler;
QQnxVirtualKeyboardBps *m_virtualKeyboard;
QHash<dialog_instance_t, QQnxFileDialogHelper*> m_dialogMapper;
};
QT_END_NAMESPACE

View File

@ -0,0 +1,309 @@
/***************************************************************************
**
** Copyright (C) 2012 Research In Motion
** Contact: http://www.qt-project.org/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qqnxfiledialoghelper.h"
#include "qqnxbpseventfilter.h"
#include "qqnxscreen.h"
#include <QDebug>
#include <QEventLoop>
#include <QScreen>
#include <QTimer>
#include <QWindow>
#ifdef QQNXFILEDIALOGHELPER_DEBUG
#define qFileDialogHelperDebug qDebug
#else
#define qFileDialogHelperDebug QT_NO_QDEBUG_MACRO
#endif
QT_BEGIN_NAMESPACE
QQnxFileDialogHelper::QQnxFileDialogHelper(QQnxBpsEventFilter *eventFilter)
: QPlatformFileDialogHelper(),
m_eventFilter(eventFilter),
m_dialog(0),
m_acceptMode(QFileDialogOptions::AcceptOpen),
m_selectedFilter(),
m_result(QPlatformDialogHelper::Rejected),
m_paths()
{
}
QQnxFileDialogHelper::~QQnxFileDialogHelper()
{
if (m_dialog)
dialog_destroy(m_dialog);
}
bool QQnxFileDialogHelper::handleEvent(bps_event_t *event)
{
qFileDialogHelperDebug() << Q_FUNC_INFO;
// Check dialog event response type (OK vs CANCEL)
// CANCEL => index = 0
// OK => index = 1
int index = dialog_event_get_selected_index(event);
qFileDialogHelperDebug() << "Index =" << index;
if (index == 1) {
m_result = QPlatformDialogHelper::Accepted;
if (m_acceptMode == QFileDialogOptions::AcceptOpen) {
// File open dialog
// ###TODO Check that this actually gets multiple paths and cleans up properly
char **filePaths = 0;
int pathCount = 0;
int result = dialog_event_get_filebrowse_filepaths(event, &filePaths, &pathCount);
if (result != BPS_SUCCESS) {
qWarning() << "Could not get paths from native file dialog";
return false;
}
for (int i = 0; i < pathCount; ++i) {
QString path = QFile::decodeName(filePaths[i]);
m_paths.append(path);
qFileDialogHelperDebug() << "path =" << path;
}
bps_free(filePaths);
} else {
// File save dialog
const char *filePath = dialog_event_get_filesave_filepath(event);
QString path = QFile::decodeName(filePath);
qFileDialogHelperDebug() << "path =" << path;
m_paths.append(path);
}
} else { // Cancel
m_result = QPlatformDialogHelper::Rejected;
}
emit dialogClosed();
return true;
}
void QQnxFileDialogHelper::exec()
{
qFileDialogHelperDebug() << Q_FUNC_INFO;
// Clear any previous results
m_paths.clear();
QEventLoop loop;
connect(this, SIGNAL(dialogClosed()), &loop, SLOT(quit()));
loop.exec();
if (m_result == QPlatformDialogHelper::Accepted)
emit accept();
else
emit reject();
}
bool QQnxFileDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent)
{
Q_UNUSED(flags);
qFileDialogHelperDebug() << Q_FUNC_INFO;
// We *really* need the bps event filter ;)
if (!m_eventFilter)
return false;
// Native dialogs can only handle application modal use cases so far
if (modality != Qt::ApplicationModal)
return false;
// Tear down any existing dialog and start again as dialog mode may have changed
if (m_dialog) {
dialog_destroy(m_dialog);
m_dialog = 0;
}
// Create dialog
const QSharedPointer<QFileDialogOptions> &opts = options();
if (opts->acceptMode() == QFileDialogOptions::AcceptOpen) {
if (dialog_create_filebrowse(&m_dialog) != BPS_SUCCESS) {
qWarning("dialog_create_filebrowse failed");
return false;
}
// Select one or many files?
bool multiSelect = (opts->fileMode() == QFileDialogOptions::ExistingFiles);
dialog_set_filebrowse_multiselect(m_dialog, multiSelect);
// Set the actual list of extensions
if (!opts->nameFilters().isEmpty()) {
qFileDialogHelperDebug() << "nameFilters =" << opts->nameFilters();
setNameFilter(opts->nameFilters().first());
} else {
QString defaultNameFilter = QStringLiteral("*.*");
setNameFilter(defaultNameFilter);
}
} else {
if (dialog_create_filesave(&m_dialog) != BPS_SUCCESS) {
qWarning("dialog_create_filesave failed");
return false;
}
// Maybe pre-select a filename
if (!opts->initiallySelectedFiles().isEmpty()) {
QString fileName = opts->initiallySelectedFiles().first();
dialog_set_filesave_filename(m_dialog, QFile::encodeName(fileName).constData());
}
// Add OK and Cancel buttons. We add the buttons in the order "CANCEL" followed by "OK
// such that they have indices matching the buttons on the file open dialog which
// is automatically populated with buttons.
if (dialog_add_button(m_dialog, tr("CANCEL").toLocal8Bit().constData(), true, 0, true) != BPS_SUCCESS) {
qWarning("dialog_add_button failed");
return false;
}
if (dialog_add_button(m_dialog, tr("OK").toLocal8Bit().constData(), true, 0, true) != BPS_SUCCESS) {
qWarning("dialog_add_button failed");
return false;
}
}
// Cache the accept mode so we know which functions to use to get the results back
m_acceptMode = opts->acceptMode();
// Set the libscreen window group and common properties
QQnxScreen *nativeScreen = static_cast<QQnxScreen *>(parent->screen()->handle());
dialog_set_group_id(m_dialog, nativeScreen->windowGroupName());
dialog_set_title_text(m_dialog, opts->windowTitle().toLocal8Bit().constData());
// Register ourselves for dialog domain events from bps
m_eventFilter->registerForDialogEvents(this);
// Show the dialog
dialog_show(m_dialog);
return true;
}
void QQnxFileDialogHelper::hide()
{
qFileDialogHelperDebug() << Q_FUNC_INFO;
if (!m_dialog)
return;
dialog_cancel(m_dialog);
}
bool QQnxFileDialogHelper::defaultNameFilterDisables() const
{
qFileDialogHelperDebug() << Q_FUNC_INFO;
return false;
}
void QQnxFileDialogHelper::setDirectory(const QString &directory)
{
qFileDialogHelperDebug() << Q_FUNC_INFO << "directory =" << directory;
// No native API for setting the directory(!). The best we can do is to
// set it as the file name but even then only with a file save dialog.
if (m_dialog && m_acceptMode == QFileDialogOptions::AcceptSave)
dialog_set_filesave_filename(m_dialog, QFile::encodeName(directory).constData());
}
QString QQnxFileDialogHelper::directory() const
{
qFileDialogHelperDebug() << Q_FUNC_INFO;
return m_paths.first();
}
void QQnxFileDialogHelper::selectFile(const QString &fileName)
{
qFileDialogHelperDebug() << Q_FUNC_INFO << "filename =" << fileName;
if (m_dialog && m_acceptMode == QFileDialogOptions::AcceptSave)
dialog_set_filesave_filename(m_dialog, QFile::encodeName(fileName).constData());
}
QStringList QQnxFileDialogHelper::selectedFiles() const
{
qFileDialogHelperDebug() << Q_FUNC_INFO;
return m_paths;
}
void QQnxFileDialogHelper::setFilter()
{
// No native api to support setting a filter from QDir::Filters
qFileDialogHelperDebug() << Q_FUNC_INFO;
}
void QQnxFileDialogHelper::selectNameFilter(const QString &filter)
{
qFileDialogHelperDebug() << Q_FUNC_INFO << "filter =" << filter;
setNameFilter(filter);
}
QString QQnxFileDialogHelper::selectedNameFilter() const
{
// For now there is no way for the user to change the selected filter
// so this just reflects what the developer has set programmatically.
qFileDialogHelperDebug() << Q_FUNC_INFO;
return m_selectedFilter;
}
void QQnxFileDialogHelper::setNameFilter(const QString &filter)
{
qFileDialogHelperDebug() << Q_FUNC_INFO << "filter =" << filter;
// Extract the globbing expressions
QStringList filters = QPlatformFileDialogHelper::cleanFilterList(filter);
char **globs = new char*[filters.size()];
for (int i = 0; i < filters.size(); ++i) {
QByteArray glob = filters.at(i).toLocal8Bit();
globs[i] = new char[glob.length()];
strcpy(globs[i], glob.constData());
}
// Set the filters
dialog_set_filebrowse_filter(m_dialog, const_cast<const char**>(globs), filters.size());
m_selectedFilter = filter;
// Cleanup
for (int i = 0; i < filters.size(); ++i)
delete[] globs[i];
delete[] globs;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,95 @@
/***************************************************************************
**
** Copyright (C) 2012 Research In Motion
** Contact: http://www.qt-project.org/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QQNXFILEDIALOGHELPER_H
#define QQNXFILEDIALOGHELPER_H
#include <qpa/qplatformdialoghelper.h>
#include <bps/dialog.h>
QT_BEGIN_NAMESPACE
class QQnxBpsEventFilter;
class QQnxFileDialogHelper : public QPlatformFileDialogHelper
{
Q_OBJECT
public:
explicit QQnxFileDialogHelper(QQnxBpsEventFilter *eventFilter);
~QQnxFileDialogHelper();
bool handleEvent(bps_event_t *event);
void exec();
bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent);
void hide();
bool defaultNameFilterDisables() const;
void setDirectory(const QString &directory);
QString directory() const;
void selectFile(const QString &fileName);
QStringList selectedFiles() const;
void setFilter();
void selectNameFilter(const QString &filter);
QString selectedNameFilter() const;
dialog_instance_t nativeDialog() const { return m_dialog; }
Q_SIGNALS:
void dialogClosed();
private:
void setNameFilter(const QString &filter);
QQnxBpsEventFilter *m_eventFilter;
dialog_instance_t m_dialog;
QFileDialogOptions::AcceptMode m_acceptMode;
QString m_selectedFilter;
QPlatformDialogHelper::DialogCode m_result;
QStringList m_paths;
};
QT_END_NAMESPACE
#endif // QQNXFILEDIALOGHELPER_H

View File

@ -382,7 +382,7 @@ QPlatformTheme *QQnxIntegration::createPlatformTheme(const QString &name) const
{
qIntegrationDebug() << Q_FUNC_INFO << "name =" << name;
if (name == QQnxTheme::name())
return new QQnxTheme(m_fontDatabase);
return new QQnxTheme(m_fontDatabase, m_bpsEventFilter);
return QPlatformIntegration::createPlatformTheme(name);
}
#endif

View File

@ -52,6 +52,7 @@ QT_BEGIN_NAMESPACE
class QQnxBpsEventFilter;
class QQnxScreenEventThread;
class QQnxFileDialogHelper;
class QQnxNativeInterface;
class QQnxWindow;
class QQnxScreen;
@ -115,6 +116,7 @@ public:
#if defined(Q_OS_BLACKBERRY)
QStringList themeNames() const;
QPlatformTheme *createPlatformTheme(const QString &name) const;
QQnxBpsEventFilter *bpsEventFilter() const { return m_bpsEventFilter; }
#endif
static QWindow *window(screen_window_t qnxWindow);

View File

@ -41,12 +41,15 @@
#include "qqnxtheme.h"
#include "qqnxfiledialoghelper.h"
#include "qqnxsystemsettings.h"
QT_BEGIN_NAMESPACE
QQnxTheme::QQnxTheme(QPlatformFontDatabase *fontDatabase)
: m_fontDatabase(fontDatabase)
QQnxTheme::QQnxTheme(QPlatformFontDatabase *fontDatabase,
QQnxBpsEventFilter *eventFilter)
: m_fontDatabase(fontDatabase),
m_eventFilter(eventFilter)
{
}
@ -55,6 +58,37 @@ QQnxTheme::~QQnxTheme()
qDeleteAll(m_fonts);
}
bool QQnxTheme::usePlatformNativeDialog(DialogType type) const
{
if (type == QPlatformTheme::FileDialog)
return true;
#if !defined(QT_NO_COLORDIALOG)
if (type == QPlatformTheme::ColorDialog)
return false;
#endif
#if !defined(QT_NO_FONTDIALOG)
if (type == QPlatformTheme::FontDialog)
return false;
#endif
return false;
}
QPlatformDialogHelper *QQnxTheme::createPlatformDialogHelper(DialogType type) const
{
switch (type) {
case QPlatformTheme::FileDialog:
return new QQnxFileDialogHelper(m_eventFilter);
#ifndef QT_NO_COLORDIALOG
case QPlatformTheme::ColorDialog:
#endif
#ifndef QT_NO_FONTDIALOG
case QPlatformTheme::FontDialog:
#endif
default:
return 0;
}
}
const QFont *QQnxTheme::font(Font type) const
{
if (m_fonts.isEmpty() && m_fontDatabase)

View File

@ -51,20 +51,26 @@
QT_BEGIN_NAMESPACE
class QQnxBpsEventFilter;
class QPlatformFontDatabase;
class QQnxTheme : public QPlatformTheme
{
public:
QQnxTheme(QPlatformFontDatabase *fontDatabase);
QQnxTheme(QPlatformFontDatabase *fontDatabase, QQnxBpsEventFilter *eventFilter);
~QQnxTheme();
static QString name() { return QStringLiteral("blackberry"); }
bool usePlatformNativeDialog(DialogType type) const;
QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const;
const QFont *font(Font type = SystemFont) const;
private:
QPlatformFontDatabase *m_fontDatabase;
QQnxBpsEventFilter *m_eventFilter;
mutable QHash<QPlatformTheme::Font, QFont*> m_fonts;
};