diff options
-rw-r--r-- | vcl/CustomTarget_qt5_moc.mk | 1 | ||||
-rw-r--r-- | vcl/Library_vclplug_qt5.mk | 1 | ||||
-rw-r--r-- | vcl/qt5/Qt5FilePicker.cxx | 787 | ||||
-rw-r--r-- | vcl/qt5/Qt5FilePicker.hxx | 244 | ||||
-rw-r--r-- | vcl/qt5/Qt5Instance.cxx | 23 | ||||
-rw-r--r-- | vcl/qt5/Qt5Instance.hxx | 6 |
6 files changed, 1058 insertions, 4 deletions
diff --git a/vcl/CustomTarget_qt5_moc.mk b/vcl/CustomTarget_qt5_moc.mk index 60d5df34d405..20b2da3ef280 100644 --- a/vcl/CustomTarget_qt5_moc.mk +++ b/vcl/CustomTarget_qt5_moc.mk @@ -10,6 +10,7 @@ $(eval $(call gb_CustomTarget_CustomTarget,vcl/qt5)) $(call gb_CustomTarget_get_target,vcl/qt5) : \ + $(call gb_CustomTarget_get_workdir,vcl/qt5)/Qt5FilePicker.moc \ $(call gb_CustomTarget_get_workdir,vcl/qt5)/Qt5Instance.moc \ $(call gb_CustomTarget_get_workdir,vcl/qt5)/Qt5Timer.moc \ $(call gb_CustomTarget_get_workdir,vcl/qt5)/Qt5Widget.moc \ diff --git a/vcl/Library_vclplug_qt5.mk b/vcl/Library_vclplug_qt5.mk index 5245d8a0149b..04fcd8d2ea9f 100644 --- a/vcl/Library_vclplug_qt5.mk +++ b/vcl/Library_vclplug_qt5.mk @@ -80,6 +80,7 @@ endif $(eval $(call gb_Library_add_exception_objects,vclplug_qt5,\ vcl/qt5/Qt5Bitmap \ vcl/qt5/Qt5Data \ + vcl/qt5/Qt5FilePicker \ vcl/qt5/Qt5Font \ vcl/qt5/Qt5FontFace \ vcl/qt5/Qt5Frame \ diff --git a/vcl/qt5/Qt5FilePicker.cxx b/vcl/qt5/Qt5FilePicker.cxx new file mode 100644 index 000000000000..cb599b6822a7 --- /dev/null +++ b/vcl/qt5/Qt5FilePicker.cxx @@ -0,0 +1,787 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "Qt5FilePicker.hxx" +#include <Qt5FilePicker.moc> + +#include "Qt5Frame.hxx" +#include "Qt5Tools.hxx" +#include "Qt5Widget.hxx" + +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/ui/dialogs/CommonFilePickerElementIds.hpp> +#include <com/sun/star/ui/dialogs/ControlActions.hpp> +#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp> +#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <cppuhelper/interfacecontainer.h> +#include <cppuhelper/supportsservice.hxx> + +#include <QtCore/QDebug> +#include <QtCore/QRegularExpression> +#include <QtCore/QThread> +#include <QtCore/QUrl> +#include <QtGui/QClipboard> +#include <QtGui/QWindow> +#include <QtWidgets/QApplication> +#include <QtWidgets/QCheckBox> +#include <QtWidgets/QComboBox> +#include <QtWidgets/QGridLayout> +#include <QtWidgets/QHBoxLayout> +#include <QtWidgets/QLabel> +#include <QtWidgets/QMessageBox> +#include <QtWidgets/QPushButton> +#include <QtWidgets/QWidget> + +#include <unx/geninst.h> +#include <strings.hrc> + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::ui::dialogs; +using namespace ::com::sun::star::ui::dialogs::TemplateDescription; +using namespace ::com::sun::star::ui::dialogs::ExtendedFilePickerElementIds; +using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; + +namespace +{ + uno::Sequence<OUString> SAL_CALL FilePicker_getSupportedServiceNames() + { + uno::Sequence<OUString> aRet(3); + aRet[0] = "com.sun.star.ui.dialogs.FilePicker"; + aRet[1] = "com.sun.star.ui.dialogs.SystemFilePicker"; + aRet[2] = "com.sun.star.ui.dialogs.Qt5FilePicker"; + return aRet; + } +} + +Qt5FilePicker::Qt5FilePicker( QFileDialog::FileMode eMode ) + : Qt5FilePicker_Base( m_aHelperMutex ) +{ +SAL_DEBUG( "Qt5FilePicker::Qt5FilePicker" ); + m_pFileDialog = new QFileDialog(); + m_pOptionsDialog = new QDialog(); + + m_pFileDialog->setFileMode( eMode ); +// m_pFileDialog->setWindowModality( Qt::WindowModal ); + m_pFileDialog->setWindowModality( Qt::ApplicationModal ); + m_pOptionsDialog->setWindowModality( Qt::WindowModal ); + + setMultiSelectionMode( false ); + + // XExecutableDialog functions + connect( this, SIGNAL( setTitleSignal( const OUString & ) ), + this, SLOT( setTitleSlot( const OUString & ) ), Qt::BlockingQueuedConnection ); + connect( this, SIGNAL( executeSignal() ), + this, SLOT( executeSlot() ), Qt::BlockingQueuedConnection ); + + // XFilePicker functions + connect( this, SIGNAL( setMultiSelectionModeSignal( bool ) ), + this, SLOT( setMultiSelectionModeSlot( bool ) ), Qt::BlockingQueuedConnection ); + connect( this, SIGNAL( setDefaultNameSignal( const OUString & ) ), + this, SLOT( setDefaultNameSlot( const OUString & ) ), Qt::BlockingQueuedConnection ); + connect( this, SIGNAL( setDisplayDirectorySignal( const OUString & ) ), + this, SLOT( setDisplayDirectorySlot( const OUString & ) ), Qt::BlockingQueuedConnection ); + connect( this, SIGNAL( getDisplayDirectorySignal() ), + this, SLOT( getDisplayDirectorySlot() ), Qt::BlockingQueuedConnection ); + connect( this, SIGNAL( getFilesSignal() ), + this, SLOT( getFilesSlot() ), Qt::BlockingQueuedConnection ); + + // XFilterManager functions + connect( this, SIGNAL( appendFilterSignal( const OUString &, const OUString & ) ), + this, SLOT( appendFilterSlot( const OUString &, const OUString & ) ), Qt::BlockingQueuedConnection ); + connect( this, SIGNAL( setCurrentFilterSignal( const OUString & ) ), + this, SLOT( setCurrentFilterSlot( const OUString & ) ), Qt::BlockingQueuedConnection ); + connect( this, SIGNAL( getCurrentFilterSignal() ), + this, SLOT( getCurrentFilterSlot() ), Qt::BlockingQueuedConnection ); + + // XFilterGroupManager functions + connect( this, SIGNAL( appendFilterGroupSignal( const OUString &, const css::uno::Sequence< css::beans::StringPair > & ) ), + this, SLOT( appendFilterGroupSlot( const OUString &, const css::uno::Sequence< css::beans::StringPair > & ) ), Qt::BlockingQueuedConnection ); + + // XFilePickerControlAccess functions + connect( this, SIGNAL( setValueSignal( sal_Int16, sal_Int16, const css::uno::Any & ) ), + this, SLOT( setValueSlot( sal_Int16, sal_Int16, const css::uno::Any & ) ), Qt::BlockingQueuedConnection ); + connect( this, SIGNAL( getValueSignal( sal_Int16, sal_Int16 ) ), + this, SLOT( getValueSlot( sal_Int16, sal_Int16 ) ), Qt::BlockingQueuedConnection ); + connect( this, SIGNAL( enableControlSignal( sal_Int16, bool ) ), + this, SLOT( enableControlSlot( sal_Int16, bool ) ), Qt::BlockingQueuedConnection ); + connect( this, SIGNAL( setLabelSignal( sal_Int16, const OUString & ) ), + this, SLOT( setLabelSlot( sal_Int16, const OUString & ) ), Qt::BlockingQueuedConnection ); + connect( this, SIGNAL( getLabelSignal( sal_Int16 ) ), + this, SLOT( getLabelSlot( sal_Int16 ) ), Qt::BlockingQueuedConnection ); + + // XFilePicker2 functions + connect( this, SIGNAL( getSelectedFilesSignal() ), + this, SLOT( getSelectedFilesSlot() ), Qt::BlockingQueuedConnection ); + + // XInitialization + connect( this, SIGNAL( initializeSignal( const css::uno::Sequence< css::uno::Any > & ) ), + this, SLOT( initializeSlot( const css::uno::Sequence< css::uno::Any > & ) ), Qt::BlockingQueuedConnection ); + + // Destructor proxy + connect( this, SIGNAL( cleanupProxySignal() ), this, SLOT( cleanupProxy() ), Qt::BlockingQueuedConnection ); + + // XFilePickerListener notifications + connect( m_pFileDialog, SIGNAL( filterSelected(const QString&) ), this, SLOT( filterSelected(const QString&) )); + connect( m_pFileDialog, SIGNAL( selectionChanged() ), this, SLOT( selectionChanged() )); +} + +Qt5FilePicker::~Qt5FilePicker() +{ + cleanupProxy(); +} + +void Qt5FilePicker::cleanupProxy() +{ + if( qApp->thread() != QThread::currentThread() ) { + SolarMutexReleaser aReleaser; + return Q_EMIT cleanupProxySignal(); + } + delete m_pOptionsDialog; +} + +void SAL_CALL Qt5FilePicker::addFilePickerListener( const uno::Reference<XFilePickerListener>& xListener ) +{ + SolarMutexGuard aGuard; + m_xListener = xListener; +} + +void SAL_CALL Qt5FilePicker::removeFilePickerListener( const uno::Reference<XFilePickerListener>& ) +{ + SolarMutexGuard aGuard; + m_xListener.clear(); +} + +void SAL_CALL Qt5FilePicker::setTitle( const OUString &title ) +{ + if( qApp->thread() != QThread::currentThread() ) { + SolarMutexReleaser aReleaser; + return Q_EMIT setTitleSignal( title ); + } + + m_pOptionsDialog->setWindowTitle( toQString(title) ); +} + +sal_Int16 SAL_CALL Qt5FilePicker::execute() +{ + if( qApp->thread() != QThread::currentThread() ) { + SolarMutexReleaser aReleaser; + return Q_EMIT executeSignal(); + } + + vcl::Window *pWindow = ::Application::GetActiveTopWindow(); + assert( pWindow ); + Qt5Widget *pTransientParent = nullptr; + QWindow *pTransientWindow = nullptr; + if( pWindow ) + { + Qt5Frame *pFrame = dynamic_cast<Qt5Frame *>( pWindow->ImplGetFrame() ); + assert( pFrame ); + if( pFrame ) + { + pTransientParent = static_cast<Qt5Widget *>( pFrame->GetQWidget() ); + pTransientWindow = pTransientParent->window()->windowHandle(); + } + } + + m_pFileDialog->setNameFilters( m_aNamedFilterList ); + if( !m_aCurrentFilterTitle.isNull() ) + { + int i = 0; + for( ; i < m_aFilterTitleList.size(); ++i ) + if( m_aCurrentFilterTitle == m_aFilterTitleList[ i ] ) + { + m_pFileDialog->selectNameFilter( m_aNamedFilterList[ i ] ); + break; + } + assert( i < m_aFilterTitleList.size() ); + } + + if( pTransientParent ) + { + m_pFileDialog->show(); + m_pFileDialog->window()->windowHandle()->setTransientParent( pTransientWindow ); + m_pFileDialog->setFocusProxy( pTransientParent ); +// pTransientParent->setMouseTracking( false ); + } + int result = m_pFileDialog->exec(); + if( pTransientParent ) +// pTransientParent->setMouseTracking( true ); + if( QFileDialog::Rejected == result ) + return ExecutableDialogResults::CANCEL; + + if ( !m_aCustomWidgetsMap.empty() ) + { + m_pFilenameLabel->setText( m_pFileDialog->selectedUrls()[ 0 ].url() ); + QString filter = m_pFileDialog->selectedNameFilter(); + int pos = filter.indexOf(" ("); + if( pos >= 0 ) + filter.truncate( pos ); + m_pFilterLabel->setText( filter ); + + if( pTransientParent ) + { + m_pOptionsDialog->show(); + m_pOptionsDialog->window()->windowHandle()->setTransientParent( pTransientWindow ); + m_pOptionsDialog->setFocusProxy( pTransientParent ); + } + result = m_pOptionsDialog->exec(); + if( QFileDialog::Rejected == result ) + return ExecutableDialogResults::CANCEL; + } + + return ExecutableDialogResults::OK; +} + +void SAL_CALL Qt5FilePicker::setMultiSelectionMode( sal_Bool multiSelect ) +{ + if( qApp->thread() != QThread::currentThread() ) { + SolarMutexReleaser aReleaser; + return Q_EMIT setMultiSelectionModeSignal( multiSelect ); + } + + if( multiSelect ) + m_pFileDialog->setFileMode( QFileDialog::ExistingFiles ); + else + m_pFileDialog->setFileMode( QFileDialog::ExistingFile ); +} + +void SAL_CALL Qt5FilePicker::setDefaultName( const OUString &name ) +{ + if( qApp->thread() != QThread::currentThread() ) { + SolarMutexReleaser aReleaser; + return Q_EMIT setDefaultNameSignal( name ); + } + m_pFileDialog->selectFile( toQString( name ) ); +} + +void SAL_CALL Qt5FilePicker::setDisplayDirectory( const OUString &dir ) +{ + if( qApp->thread() != QThread::currentThread() ) { + SolarMutexReleaser aReleaser; + return Q_EMIT setDisplayDirectorySignal( dir ); + } + m_pFileDialog->setDirectory( toQString( dir ) ); +} + +OUString SAL_CALL Qt5FilePicker::getDisplayDirectory() +{ + if( qApp->thread() != QThread::currentThread() ) { + SolarMutexReleaser aReleaser; + return Q_EMIT getDisplayDirectorySignal(); + } + return toOUString( m_pFileDialog->directoryUrl().toString() ); +} + +uno::Sequence< OUString > SAL_CALL Qt5FilePicker::getFiles() +{ + if( qApp->thread() != QThread::currentThread() ) { + SolarMutexReleaser aReleaser; + return Q_EMIT getFilesSignal(); + } + uno::Sequence< OUString > seq = getSelectedFiles(); + if (seq.getLength() > 1) + seq.realloc(1); + return seq; +} + +uno::Sequence< OUString > SAL_CALL Qt5FilePicker::getSelectedFiles() +{ + if( qApp->thread() != QThread::currentThread() ) { + SolarMutexReleaser aReleaser; + return Q_EMIT getSelectedFilesSignal(); + } + QList<QUrl> urls = m_pFileDialog->selectedUrls(); + uno::Sequence< OUString > seq( urls.size() ); + int i = 0; + foreach( const QUrl& url, urls ) + seq[ i++ ]= toOUString( url.toString() ); + return seq; +} + +void SAL_CALL Qt5FilePicker::appendFilter( const OUString &title, const OUString &filter ) +{ + if( qApp->thread() != QThread::currentThread() ) { + SolarMutexReleaser aReleaser; + return Q_EMIT appendFilterSignal( title, filter ); + } + + QString t = toQString(title); + QString f = toQString(filter); + + // '/' need to be escaped else they are assumed to be mime types by kfiledialog + //see the docs + t.replace("/", "\\/"); + + int pos = t.indexOf(" ("); + if( pos >= 0 ) + t.truncate( pos ); + + // openoffice gives us filters separated by ';' qt dialogs just want space separated + f.replace(";", " "); + + // make sure "*.*" is not used as "all files" + f.replace("*.*", "*"); + + m_aFilterTitleList.append( toQString( title ) ); + m_aNamedFilterList.append( QString("%1 (%2)").arg(t).arg(f) ); +} + +void SAL_CALL Qt5FilePicker::setCurrentFilter( const OUString &title ) +{ + if( qApp->thread() != QThread::currentThread() ) { + SolarMutexReleaser aReleaser; + return Q_EMIT setCurrentFilterSignal( title ); + } + + m_aCurrentFilterTitle = toQString( title ); +} + +OUString SAL_CALL Qt5FilePicker::getCurrentFilter() +{ + if( qApp->thread() != QThread::currentThread() ) { + SolarMutexReleaser aReleaser; + return Q_EMIT getCurrentFilterSignal(); + } + + QString filter = m_pFileDialog->selectedNameFilter(); + for( int i = 0; i < m_aNamedFilterList.size(); ++i ) + if( filter == m_aNamedFilterList[ i ] ) + return toOUString( m_aFilterTitleList[ i ] ); + SAL_DEBUG( "vcl.qt5 " << m_aNamedFilterList.size() << " Unknown filter: " << toOUString( filter ) ); + assert( !"Selected filter not in filter list?! " ); + return OUString( "" ); +} + +void SAL_CALL Qt5FilePicker::appendFilterGroup( const OUString& rGroupTitle, const uno::Sequence<beans::StringPair>& filters) +{ + if( qApp->thread() != QThread::currentThread() ) { + SolarMutexReleaser aReleaser; + return Q_EMIT appendFilterGroupSignal( rGroupTitle, filters ); + } + + const sal_uInt16 length = filters.getLength(); + for (sal_uInt16 i = 0; i < length; ++i) + { + beans::StringPair aPair = filters[i]; + appendFilter( aPair.First, aPair.Second ); + } +} + +void SAL_CALL Qt5FilePicker::setValue( sal_Int16 controlId, sal_Int16 nControlAction, const uno::Any &value ) +{ + if( qApp->thread() != QThread::currentThread() ) { + SolarMutexReleaser aReleaser; + return Q_EMIT setValueSignal( controlId, nControlAction, value ); + } + + if (m_aCustomWidgetsMap.contains( controlId )) { + QCheckBox* cb = dynamic_cast<QCheckBox*>( m_aCustomWidgetsMap.value( controlId )); + if (cb) + cb->setChecked(value.get<bool>()); + } + else + SAL_WARN( "vcl", "set label on unknown control " << controlId ); +} + +uno::Any SAL_CALL Qt5FilePicker::getValue( sal_Int16 controlId, sal_Int16 nControlAction ) +{ + if (CHECKBOX_AUTOEXTENSION == controlId) + // We ignore this one and rely on QFileDialog to provide the function. + // Always return false, to pretend we do not support this, otherwise + // LO core would try to be smart and cut the extension in some places, + // interfering with QFileDialog's handling of it. QFileDialog also + // saves the value of the setting, so LO core is not needed for that either. + return uno::Any( false ); + + if( qApp->thread() != QThread::currentThread() ) { + SolarMutexReleaser aReleaser; + return Q_EMIT getValueSignal( controlId, nControlAction ); + } + + uno::Any res(false); + if (m_aCustomWidgetsMap.contains( controlId )) { + QCheckBox* cb = dynamic_cast<QCheckBox*>( m_aCustomWidgetsMap.value( controlId )); + if (cb) + res <<= cb->isChecked(); + } + else + SAL_WARN( "vcl", "get value on unknown control " << controlId ); + + return res; +} + +void SAL_CALL Qt5FilePicker::enableControl( sal_Int16 controlId, sal_Bool enable ) +{ + if( qApp->thread() != QThread::currentThread() ) { + SolarMutexReleaser aReleaser; + return Q_EMIT enableControlSignal( controlId, enable ); + } + + if (m_aCustomWidgetsMap.contains( controlId )) + m_aCustomWidgetsMap.value( controlId )->setEnabled( enable ); + else + SAL_WARN( "vcl", "enable unknown control " << controlId ); +} + +void SAL_CALL Qt5FilePicker::setLabel( sal_Int16 controlId, const OUString &label ) +{ + if( qApp->thread() != QThread::currentThread() ) { + SolarMutexReleaser aReleaser; + return Q_EMIT setLabelSignal( controlId, label ); + } + + if (m_aCustomWidgetsMap.contains( controlId )) { + QCheckBox* cb = dynamic_cast<QCheckBox*>( m_aCustomWidgetsMap.value( controlId )); + if (cb) + cb->setText( toQString(label) ); + } + else + SAL_WARN( "vcl", "set label on unknown control " << controlId ); +} + +OUString SAL_CALL Qt5FilePicker::getLabel(sal_Int16 controlId) +{ + if( qApp->thread() != QThread::currentThread() ) { + SolarMutexReleaser aReleaser; + return Q_EMIT getLabelSignal( controlId ); + } + + QString label; + if (m_aCustomWidgetsMap.contains( controlId )) { + QCheckBox* cb = dynamic_cast<QCheckBox*>( m_aCustomWidgetsMap.value( controlId )); + if (cb) + label = cb->text(); + } + else + SAL_WARN( "vcl", "get label on unknown control " << controlId ); + + return toOUString(label); +} + +QString Qt5FilePicker::getResString(const char *pResId) +{ + QString aResString; + + if (pResId == nullptr) + return aResString; + + aResString = toQString(VclResId(pResId)); + + return aResString.replace('~', '&'); +} + +void Qt5FilePicker::addCustomControl(QGridLayout *pLayout, sal_Int16 controlId) +{ + QWidget* widget = nullptr; + QWidget* label = nullptr; + const char* resId = nullptr; + + switch (controlId) + { + case CHECKBOX_AUTOEXTENSION: + resId = STR_FPICKER_AUTO_EXTENSION; + break; + case CHECKBOX_PASSWORD: + resId = STR_FPICKER_PASSWORD; + break; + case CHECKBOX_FILTEROPTIONS: + resId = STR_FPICKER_FILTER_OPTIONS; + break; + case CHECKBOX_READONLY: + resId = STR_FPICKER_READONLY; + break; + case CHECKBOX_LINK: + resId = STR_FPICKER_INSERT_AS_LINK; + break; + case CHECKBOX_PREVIEW: + resId = STR_FPICKER_SHOW_PREVIEW; + break; + case CHECKBOX_SELECTION: + resId = STR_FPICKER_SELECTION; + break; + case PUSHBUTTON_PLAY: + resId = STR_FPICKER_PLAY; + break; + case LISTBOX_VERSION: + resId = STR_FPICKER_VERSION; + break; + case LISTBOX_TEMPLATE: + resId = STR_FPICKER_TEMPLATES; + break; + case LISTBOX_IMAGE_TEMPLATE: + resId = STR_FPICKER_IMAGE_TEMPLATE; + break; + case LISTBOX_VERSION_LABEL: + case LISTBOX_TEMPLATE_LABEL: + case LISTBOX_IMAGE_TEMPLATE_LABEL: + case LISTBOX_FILTER_SELECTOR: + break; + } + + switch (controlId) + { + case CHECKBOX_AUTOEXTENSION: + case CHECKBOX_PASSWORD: + case CHECKBOX_FILTEROPTIONS: + case CHECKBOX_READONLY: + case CHECKBOX_LINK: + case CHECKBOX_PREVIEW: + case CHECKBOX_SELECTION: + widget = new QCheckBox(getResString(resId), m_pOptionsDialog); + break; + case PUSHBUTTON_PLAY: + break; + case LISTBOX_VERSION: + case LISTBOX_TEMPLATE: + case LISTBOX_IMAGE_TEMPLATE: + case LISTBOX_FILTER_SELECTOR: + label = new QLabel( getResString(resId), m_pOptionsDialog ); + widget = new QComboBox( m_pOptionsDialog ); + break; + case LISTBOX_VERSION_LABEL: + case LISTBOX_TEMPLATE_LABEL: + case LISTBOX_IMAGE_TEMPLATE_LABEL: + break; + } + + if( widget ) + { + const int row = pLayout->rowCount(); + if( label ) + pLayout->addWidget( label, row, 0 ); + pLayout->addWidget( widget, row, 1 ); + m_aCustomWidgetsMap.insert( controlId, widget ); + } +} + +void SAL_CALL Qt5FilePicker::initialize( const uno::Sequence<uno::Any> &args ) +{ + if( qApp->thread() != QThread::currentThread() ) { + SolarMutexReleaser aReleaser; + return Q_EMIT initializeSignal( args ); + } + + m_aNamedFilterList.clear(); + m_aFilterTitleList.clear(); + m_aCurrentFilterTitle.clear(); + + // parameter checking + uno::Any arg; + if (args.getLength() == 0) + { + throw lang::IllegalArgumentException( + "no arguments", + static_cast< XFilePicker2* >( this ), 1 ); + } + + arg = args[0]; + + if (( arg.getValueType() != cppu::UnoType<sal_Int16>::get()) && + ( arg.getValueType() != cppu::UnoType<sal_Int8>::get())) + { + throw lang::IllegalArgumentException( + "invalid argument type", + static_cast< XFilePicker2* >( this ), 1 ); + } + + QGridLayout *pLayout = new QGridLayout(); + m_pOptionsDialog->setLayout( pLayout ); + pLayout->addWidget( new QLabel( "Filename:" ), 0, 0 ); + m_pFilenameLabel = new QLabel(); + pLayout->addWidget( m_pFilenameLabel, 0, 1 ); + pLayout->addWidget( new QLabel( "Type:" ), 1, 0 ); + m_pFilterLabel = new QLabel(); + pLayout->addWidget( m_pFilterLabel, 1, 1 ); + + sal_Int16 templateId = -1; + arg >>= templateId; + + QFileDialog::AcceptMode acceptMode = QFileDialog::AcceptOpen; + switch ( templateId ) + { + case FILEOPEN_SIMPLE: + break; + + case FILESAVE_SIMPLE: + acceptMode = QFileDialog::AcceptSave; + break; + + case FILESAVE_AUTOEXTENSION: + acceptMode = QFileDialog::AcceptSave; + addCustomControl( pLayout, CHECKBOX_AUTOEXTENSION ); + break; + + case FILESAVE_AUTOEXTENSION_PASSWORD: + acceptMode = QFileDialog::AcceptSave; + addCustomControl( pLayout, CHECKBOX_AUTOEXTENSION ); + addCustomControl( pLayout, CHECKBOX_PASSWORD ); + break; + + case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS: + acceptMode = QFileDialog::AcceptSave; + addCustomControl( pLayout, CHECKBOX_AUTOEXTENSION ); + addCustomControl( pLayout, CHECKBOX_PASSWORD ); + addCustomControl( pLayout, CHECKBOX_FILTEROPTIONS ); + break; + + case FILESAVE_AUTOEXTENSION_SELECTION: + acceptMode = QFileDialog::AcceptSave; + addCustomControl( pLayout, CHECKBOX_AUTOEXTENSION ); + addCustomControl( pLayout, CHECKBOX_SELECTION ); + break; + + case FILESAVE_AUTOEXTENSION_TEMPLATE: + acceptMode = QFileDialog::AcceptSave; + addCustomControl( pLayout, CHECKBOX_AUTOEXTENSION ); + addCustomControl( pLayout, LISTBOX_TEMPLATE ); + break; + + case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE: + addCustomControl( pLayout, CHECKBOX_LINK ); + addCustomControl( pLayout, CHECKBOX_PREVIEW ); + addCustomControl( pLayout, LISTBOX_IMAGE_TEMPLATE ); + break; + + case FILEOPEN_PLAY: + addCustomControl( pLayout, PUSHBUTTON_PLAY ); + break; + + case FILEOPEN_LINK_PLAY: + addCustomControl( pLayout, CHECKBOX_LINK ); + addCustomControl( pLayout, PUSHBUTTON_PLAY ); + break; + + case FILEOPEN_READONLY_VERSION: + addCustomControl( pLayout, CHECKBOX_READONLY ); + addCustomControl( pLayout, LISTBOX_VERSION ); + break; + + case FILEOPEN_LINK_PREVIEW: + addCustomControl( pLayout, CHECKBOX_LINK ); + addCustomControl( pLayout, CHECKBOX_PREVIEW ); + break; + + case FILEOPEN_PREVIEW: + addCustomControl( pLayout, CHECKBOX_PREVIEW ); + break; + + default: + throw lang::IllegalArgumentException( + "Unknown template", + static_cast< XFilePicker2* >( this ), + 1 ); + } + + if( !m_aCustomWidgetsMap.empty() ) + { + QHBoxLayout *pHBoxLayout = new QHBoxLayout(); + pLayout->addLayout( pHBoxLayout, pLayout->rowCount(), 0, 1, 2 ); + pHBoxLayout->addStretch(); + QPushButton *pButton = new QPushButton( "Ok" ); + connect( pButton, SIGNAL(clicked()), m_pOptionsDialog, SLOT(accept()) ); + pHBoxLayout->addWidget( pButton ); + pButton = new QPushButton( "Cancel" ); + connect( pButton, SIGNAL(clicked()), m_pOptionsDialog, SLOT(reject()) ); + pHBoxLayout->addWidget( pButton ); + } + + const char *resId = nullptr; + switch ( acceptMode ) + { + case QFileDialog::AcceptOpen: + resId = STR_FPICKER_OPEN; + break; + case QFileDialog::AcceptSave: + resId = STR_FPICKER_SAVE; + m_pFileDialog->setFileMode( QFileDialog::AnyFile ); + break; + } + + m_pFileDialog->setAcceptMode( acceptMode ); + m_pFileDialog->setWindowTitle( getResString(resId) ); +} + +void SAL_CALL Qt5FilePicker::cancel() +{ + +} + +void SAL_CALL Qt5FilePicker::disposing( const lang::EventObject &rEvent ) +{ + uno::Reference<XFilePickerListener> xFilePickerListener( rEvent.Source, uno::UNO_QUERY ); + + if ( xFilePickerListener.is() ) + { + removeFilePickerListener( xFilePickerListener ); + } +} + +OUString SAL_CALL Qt5FilePicker::getImplementationName() +{ + return OUString( "com.sun.star.ui.dialogs.Qt5FilePicker" ); +} + +sal_Bool SAL_CALL Qt5FilePicker::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL Qt5FilePicker::getSupportedServiceNames() +{ + return FilePicker_getSupportedServiceNames(); +} + +void Qt5FilePicker::filterSelected(const QString &) +{ + FilePickerEvent aEvent; + aEvent.ElementId = LISTBOX_FILTER; + SAL_INFO( "vcl", "filter changed" ); + if (m_xListener.is()) + m_xListener->controlStateChanged( aEvent ); +} + +void Qt5FilePicker::selectionChanged() +{ + FilePickerEvent aEvent; + SAL_INFO( "vcl", "file selection changed" ); + if (m_xListener.is()) + m_xListener->fileSelectionChanged( aEvent ); +} + +OUString Qt5FilePicker::getDirectory() +{ + uno::Sequence< OUString > seq = getSelectedFiles(); + if (seq.getLength() > 1) + seq.realloc(1); + return seq[ 0 ]; +} + +void Qt5FilePicker::setDescription( const OUString& ) +{ + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/qt5/Qt5FilePicker.hxx b/vcl/qt5/Qt5FilePicker.hxx new file mode 100644 index 000000000000..1aa19b3459f4 --- /dev/null +++ b/vcl/qt5/Qt5FilePicker.hxx @@ -0,0 +1,244 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <cppuhelper/compbase.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> +#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp> +#include <com/sun/star/ui/dialogs/XFolderPicker2.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <osl/conditn.hxx> +#include <osl/mutex.hxx> + +#include <rtl/ustrbuf.hxx> + +#include <QtCore/QObject> +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QHash> +#include <QtWidgets/QFileDialog> + +class QGridLayout; +class QLabel; +class QWidget; + +typedef ::cppu::WeakComponentImplHelper + < css::ui::dialogs::XFilePicker3 + , css::ui::dialogs::XFilePickerControlAccess + , css::ui::dialogs::XFolderPicker2 + , css::lang::XInitialization + , css::lang::XServiceInfo + > Qt5FilePicker_Base; + +class Qt5FilePicker + : public QObject + , public Qt5FilePicker_Base +{ + Q_OBJECT + +protected: + css::uno::Reference< css::ui::dialogs::XFilePickerListener > m_xListener; + + QFileDialog* m_pFileDialog; ///< the non-extendable file picker dialog + + osl::Mutex m_aHelperMutex; ///< mutex used by the WeakComponentImplHelper + + QStringList m_aNamedFilterList; ///< named filter list for the QFileDialog + QStringList m_aFilterTitleList; ///< the original filter titles + QString m_aCurrentFilterTitle; ///< the current filter title + + QDialog *m_pOptionsDialog; ///< an extra options dialog window + QLabel *m_pFilenameLabel; ///< label to display the filename + QLabel *m_pFilterLabel; ///< label to display the filter + QHash<sal_Int16, QWidget*> m_aCustomWidgetsMap; ///< map of SAL control ID's to widget + +public: + explicit Qt5FilePicker( QFileDialog::FileMode ); + virtual ~Qt5FilePicker() override; + + // XFilePickerNotifier + virtual void SAL_CALL addFilePickerListener( const css::uno::Reference< css::ui::dialogs::XFilePickerListener >& xListener ) override; + virtual void SAL_CALL removeFilePickerListener( const css::uno::Reference< css::ui::dialogs::XFilePickerListener >& xListener ) override; + + // XFilterManager functions + virtual void SAL_CALL appendFilter( const OUString &rTitle, const OUString &rFilter ) override; + virtual void SAL_CALL setCurrentFilter( const OUString &rTitle ) override; + virtual OUString SAL_CALL getCurrentFilter() override; + + // XFilterGroupManager functions + virtual void SAL_CALL appendFilterGroup( const OUString &rGroupTitle, const css::uno::Sequence< css::beans::StringPair > &rFilters ) override; + + // XCancellable + virtual void SAL_CALL cancel( ) override; + + // XExecutableDialog functions + virtual void SAL_CALL setTitle( const OUString &rTitle ) override; + virtual sal_Int16 SAL_CALL execute() override; + + // XFilePicker functions + virtual void SAL_CALL setMultiSelectionMode( sal_Bool bMode ) override; + virtual void SAL_CALL setDefaultName( const OUString &rName ) override; + virtual void SAL_CALL setDisplayDirectory( const OUString &rDirectory ) override; + virtual OUString SAL_CALL getDisplayDirectory() override; + virtual css::uno::Sequence< OUString > SAL_CALL getFiles() override; + + // XFilePickerControlAccess functions + virtual void SAL_CALL setValue( sal_Int16 nControlId, sal_Int16 nControlAction, const css::uno::Any &rValue ) override; + virtual css::uno::Any SAL_CALL getValue( sal_Int16 nControlId, sal_Int16 nControlAction ) override; + virtual void SAL_CALL enableControl( sal_Int16 nControlId, sal_Bool bEnable ) override; + virtual void SAL_CALL setLabel( sal_Int16 nControlId, const OUString &rLabel ) override; + virtual OUString SAL_CALL getLabel( sal_Int16 nControlId ) override; + + // XFilePicker2 functions + virtual css::uno::Sequence< OUString > SAL_CALL getSelectedFiles() override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any > &rArguments ) override; + + // XEventListener + /// @throws css::uno::RuntimeException + virtual void SAL_CALL disposing( const css::lang::EventObject &rEvent ); + using cppu::WeakComponentImplHelperBase::disposing; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString &rServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XFolderPicker functions + virtual OUString SAL_CALL getDirectory() override; + virtual void SAL_CALL setDescription( const OUString& rDescription ) override; + +private Q_SLOTS: + // XExecutableDialog functions + /// @throws css::uno::RuntimeException + void setTitleSlot( const OUString &rTitle ) { return setTitle( rTitle ); } + /// @throws css::uno::RuntimeException + sal_Int16 executeSlot() { return execute(); } + + // XFilePicker functions + /// @throws css::uno::RuntimeException + void setMultiSelectionModeSlot( bool bMode ) { return setMultiSelectionMode( bMode ); } + /// @throws css::uno::RuntimeException + void setDefaultNameSlot( const OUString &rName ) { return setDefaultName( rName ); } + /// @throws css::uno::RuntimeException + void setDisplayDirectorySlot( const OUString &rDirectory ) { return setDisplayDirectory( rDirectory ); } + /// @throws css::uno::RuntimeException + OUString getDisplayDirectorySlot() { return getDisplayDirectory(); } + /// @throws css::uno::RuntimeException + css::uno::Sequence< OUString > getFilesSlot() { return getFiles(); } + + // XFilterManager functions + /// @throws css::lang::IllegalArgumentException + /// @throws css::uno::RuntimeException + void appendFilterSlot( const OUString &rTitle, const OUString &rFilter ) { return appendFilter( rTitle, rFilter ); } + /// @throws css::lang::IllegalArgumentException + /// @throws css::uno::RuntimeException + void setCurrentFilterSlot( const OUString &rTitle ) { return setCurrentFilter( rTitle ); } + /// @throws css::uno::RuntimeException + OUString getCurrentFilterSlot() { return getCurrentFilter(); } + + // XFilterGroupManager functions + /// @throws css::lang::IllegalArgumentException + /// @throws css::uno::RuntimeException + void appendFilterGroupSlot( const OUString &rGroupTitle, const css::uno::Sequence< css::beans::StringPair > &rFilters ) { return appendFilterGroup( rGroupTitle, rFilters ); } + + // XFilePickerControlAccess functions + /// @throws css::uno::RuntimeException + void setValueSlot( sal_Int16 nControlId, sal_Int16 nControlAction, const css::uno::Any &rValue ) { return setValue( nControlId, nControlAction, rValue ); } + /// @throws css::uno::RuntimeException + css::uno::Any getValueSlot( sal_Int16 nControlId, sal_Int16 nControlAction ) { return getValue( nControlId, nControlAction ); } + /// @throws css::uno::RuntimeException + void enableControlSlot( sal_Int16 nControlId, bool bEnable ) { return enableControl( nControlId, bEnable ); } + /// @throws css::uno::RuntimeException + void setLabelSlot( sal_Int16 nControlId, const OUString &rLabel ) { return setLabel( nControlId, rLabel ); } + /// @throws css::uno::RuntimeException + OUString getLabelSlot( sal_Int16 nControlId ) { return getLabel( nControlId ); } + + // XFilePicker2 functions + /// @throws css::uno::RuntimeException + css::uno::Sequence< OUString > getSelectedFilesSlot() { return getSelectedFiles(); } + + // XInitialization + /// @throws css::uno::Exception + /// @throws css::uno::RuntimeException + void initializeSlot( const css::uno::Sequence< css::uno::Any > &rArguments ) { return initialize( rArguments ); } + +Q_SIGNALS: + // XExecutableDialog functions + void setTitleSignal( const OUString &rTitle ); + sal_Int16 executeSignal(); + + // XFilePicker functions + void setMultiSelectionModeSignal( bool bMode ); + void setDefaultNameSignal( const OUString &rName ); + void setDisplayDirectorySignal( const OUString &rDirectory ); + OUString getDisplayDirectorySignal(); + css::uno::Sequence< OUString > getFilesSignal(); + + // XFilterManager functions + void appendFilterSignal( const OUString &rTitle, const OUString &rFilter ); + void setCurrentFilterSignal( const OUString &rTitle ); + OUString getCurrentFilterSignal(); + + // XFilterGroupManager functions + void appendFilterGroupSignal( const OUString &rGroupTitle, const css::uno::Sequence< css::beans::StringPair > &rFilters ); + + // XFilePickerControlAccess functions + void setValueSignal( sal_Int16 nControlId, sal_Int16 nControlAction, const css::uno::Any &rValue ); + css::uno::Any getValueSignal( sal_Int16 nControlId, sal_Int16 nControlAction ); + void enableControlSignal( sal_Int16 nControlId, bool bEnable ); + void setLabelSignal( sal_Int16 nControlId, const OUString &rLabel ); + OUString getLabelSignal( sal_Int16 nControlId ); + + // XFilePicker2 functions + css::uno::Sequence< OUString > getSelectedFilesSignal() ; + + // XInitialization + void initializeSignal( const css::uno::Sequence< css::uno::Any > &rArguments ); + + // Destructor proxy + void cleanupProxySignal(); + +private: + Qt5FilePicker( const Qt5FilePicker& ) = delete; + Qt5FilePicker& operator=( const Qt5FilePicker& ) = delete; + + // add a custom control widget to the options dialog + void addCustomControl(QGridLayout *pLayout, sal_Int16 controlId); + + static QString getResString(const char* pRedId); + +private Q_SLOTS: + void cleanupProxy(); + + // emit XFilePickerListener controlStateChanged event + void filterSelected(const QString &); + // emit XFilePickerListener fileSelectionChanged event +// void urlSelected(const QUrl &); + + void selectionChanged(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/qt5/Qt5Instance.cxx b/vcl/qt5/Qt5Instance.cxx index 2a00cabe2d2c..f6bb6d855086 100644 --- a/vcl/qt5/Qt5Instance.cxx +++ b/vcl/qt5/Qt5Instance.cxx @@ -20,19 +20,20 @@ #include "Qt5Instance.hxx" #include <Qt5Instance.moc> -#include "Qt5Frame.hxx" +#include "Qt5Bitmap.hxx" #include "Qt5Data.hxx" +#include "Qt5FilePicker.hxx" +#include "Qt5Frame.hxx" +#include "Qt5Object.hxx" #include "Qt5Timer.hxx" #include "Qt5VirtualDevice.hxx" -#include "Qt5Object.hxx" -#include "Qt5Bitmap.hxx" #include <headless/svpvd.hxx> +#include <QtCore/QAbstractEventDispatcher> #include <QtCore/QThread> #include <QtWidgets/QApplication> #include <QtWidgets/QWidget> -#include <QtCore/QAbstractEventDispatcher> #include <vclpluginapi.h> #include <sal/log.hxx> @@ -183,6 +184,20 @@ void Qt5Instance::ProcessEvent(SalUserEvent aEvent) aEvent.m_pFrame->CallCallback(aEvent.m_nEvent, aEvent.m_pData); } +css::uno::Reference< css::ui::dialogs::XFilePicker2 > +Qt5Instance::createFilePicker( const css::uno::Reference< css::uno::XComponentContext > & ) +{ + return css::uno::Reference< css::ui::dialogs::XFilePicker2 >( + new Qt5FilePicker( QFileDialog::ExistingFile ) ); +} + +css::uno::Reference< css::ui::dialogs::XFolderPicker2 > +Qt5Instance::createFolderPicker( const css::uno::Reference< css::uno::XComponentContext > & ) +{ + return css::uno::Reference< css::ui::dialogs::XFolderPicker2 >( + new Qt5FilePicker( QFileDialog::Directory ) ); +} + extern "C" { VCLPLUG_QT5_PUBLIC SalInstance* create_SalInstance() { diff --git a/vcl/qt5/Qt5Instance.hxx b/vcl/qt5/Qt5Instance.hxx index 79d3fb58a5f8..0a4892c15533 100644 --- a/vcl/qt5/Qt5Instance.hxx +++ b/vcl/qt5/Qt5Instance.hxx @@ -100,6 +100,12 @@ public: virtual void TriggerUserEventProcessing() override; virtual void ProcessEvent(SalUserEvent aEvent) override; + + virtual css::uno::Reference< css::ui::dialogs::XFilePicker2 > createFilePicker( + const css::uno::Reference< css::uno::XComponentContext >& ) override; + + virtual css::uno::Reference< css::ui::dialogs::XFolderPicker2 > createFolderPicker( + const css::uno::Reference< css::uno::XComponentContext >& ) override; }; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |