If the XInitialization::initialize has a parent option, use that system window ID to find the parent Qt5Frame and set this as the parent of the file picker. This way the file picker doesn't show up as a separate window in the KDE task bar and get the proper icon. Just setting it transient to the parent is not enough. This also includes the terminate listener handling, so an open file picker won't prevent LO to shut down gracefully (which is handled independent from the document modified state). Change-Id: I62aef532e739e7fea9fe8a4b4e6c0e205faae1d9 Reviewed-on: https://gerrit.libreoffice.org/74544 Tested-by: Jenkins Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
941 lines
30 KiB
C++
941 lines
30 KiB
C++
/* -*- 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 <Qt5Instance.hxx>
|
|
|
|
#include <com/sun/star/awt/SystemDependentXWindow.hpp>
|
|
#include <com/sun/star/awt/XSystemDependentWindowPeer.hpp>
|
|
#include <com/sun/star/awt/XWindow.hpp>
|
|
#include <com/sun/star/frame/Desktop.hpp>
|
|
#include <com/sun/star/frame/TerminationVetoException.hpp>
|
|
#include <com/sun/star/frame/XDesktop.hpp>
|
|
#include <com/sun/star/lang/DisposedException.hpp>
|
|
#include <com/sun/star/lang/IllegalArgumentException.hpp>
|
|
#include <com/sun/star/lang/SystemDependent.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 <com/sun/star/uri/ExternalUriReferenceTranslator.hpp>
|
|
#include <cppuhelper/interfacecontainer.h>
|
|
#include <cppuhelper/supportsservice.hxx>
|
|
#include <rtl/process.h>
|
|
#include <sal/log.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> FilePicker_getSupportedServiceNames()
|
|
{
|
|
return { "com.sun.star.ui.dialogs.FilePicker", "com.sun.star.ui.dialogs.SystemFilePicker",
|
|
"com.sun.star.ui.dialogs.Qt5FilePicker" };
|
|
}
|
|
}
|
|
|
|
Qt5FilePicker::Qt5FilePicker(css::uno::Reference<css::uno::XComponentContext> const& context,
|
|
QFileDialog::FileMode eMode, bool bUseNative)
|
|
: Qt5FilePicker_Base(m_aHelperMutex)
|
|
, m_context(context)
|
|
, m_bIsFolderPicker(eMode == QFileDialog::Directory)
|
|
, m_pParentWidget(nullptr)
|
|
, m_pFileDialog(new QFileDialog(nullptr, {}, QDir::homePath()))
|
|
, m_pExtraControls(new QWidget())
|
|
{
|
|
m_pFileDialog->setOption(QFileDialog::DontUseNativeDialog, !bUseNative);
|
|
|
|
m_pFileDialog->setFileMode(eMode);
|
|
m_pFileDialog->setWindowModality(Qt::ApplicationModal);
|
|
|
|
if (m_bIsFolderPicker)
|
|
{
|
|
m_pFileDialog->setOption(QFileDialog::ShowDirsOnly, true);
|
|
m_pFileDialog->setWindowTitle(toQString(VclResId(STR_FPICKER_FOLDER_DEFAULT_TITLE)));
|
|
}
|
|
|
|
m_pLayout = dynamic_cast<QGridLayout*>(m_pFileDialog->layout());
|
|
|
|
setMultiSelectionMode(false);
|
|
|
|
// XFilePickerListener notifications
|
|
connect(m_pFileDialog.get(), SIGNAL(filterSelected(const QString&)), this,
|
|
SLOT(filterSelected(const QString&)));
|
|
connect(m_pFileDialog.get(), SIGNAL(currentChanged(const QString&)), this,
|
|
SLOT(currentChanged(const QString&)));
|
|
|
|
// update automatic file extension when filter is changed
|
|
connect(m_pFileDialog.get(), SIGNAL(filterSelected(const QString&)), this,
|
|
SLOT(updateAutomaticFileExtension()));
|
|
}
|
|
|
|
Qt5FilePicker::~Qt5FilePicker()
|
|
{
|
|
SolarMutexGuard g;
|
|
auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
pSalInst->RunInMainThread([this]() {
|
|
// must delete it in main thread, otherwise
|
|
// QSocketNotifier::setEnabled() will crash us
|
|
m_pFileDialog.reset();
|
|
});
|
|
}
|
|
|
|
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)
|
|
{
|
|
SolarMutexGuard g;
|
|
auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
pSalInst->RunInMainThread(
|
|
[this, &title]() { m_pFileDialog->setWindowTitle(toQString(title)); });
|
|
}
|
|
|
|
sal_Int16 SAL_CALL Qt5FilePicker::execute()
|
|
{
|
|
SolarMutexGuard g;
|
|
auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
if (!pSalInst->IsMainThread())
|
|
{
|
|
sal_uInt16 ret;
|
|
pSalInst->RunInMainThread([&ret, this]() { ret = execute(); });
|
|
return ret;
|
|
}
|
|
|
|
QWidget* pTransientParent = m_pParentWidget;
|
|
if (!pTransientParent)
|
|
{
|
|
vcl::Window* pWindow = ::Application::GetActiveTopWindow();
|
|
if (pWindow)
|
|
{
|
|
Qt5Frame* pFrame = dynamic_cast<Qt5Frame*>(pWindow->ImplGetFrame());
|
|
assert(pFrame);
|
|
if (pFrame)
|
|
pTransientParent = pFrame->asChild();
|
|
}
|
|
}
|
|
|
|
if (!m_aNamedFilterList.isEmpty())
|
|
m_pFileDialog->setNameFilters(m_aNamedFilterList);
|
|
if (!m_aCurrentFilter.isEmpty())
|
|
m_pFileDialog->selectNameFilter(m_aCurrentFilter);
|
|
|
|
updateAutomaticFileExtension();
|
|
|
|
uno::Reference<css::frame::XDesktop> xDesktop(css::frame::Desktop::create(m_context),
|
|
UNO_QUERY_THROW);
|
|
|
|
// will hide the window, so do before show
|
|
m_pFileDialog->setParent(pTransientParent, m_pFileDialog->windowFlags());
|
|
m_pFileDialog->show();
|
|
xDesktop->addTerminateListener(this);
|
|
int result = m_pFileDialog->exec();
|
|
xDesktop->removeTerminateListener(this);
|
|
m_pFileDialog->setParent(nullptr, m_pFileDialog->windowFlags());
|
|
|
|
if (QFileDialog::Rejected == result)
|
|
return ExecutableDialogResults::CANCEL;
|
|
return ExecutableDialogResults::OK;
|
|
}
|
|
|
|
void SAL_CALL Qt5FilePicker::setMultiSelectionMode(sal_Bool multiSelect)
|
|
{
|
|
SolarMutexGuard g;
|
|
auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
pSalInst->RunInMainThread([this, multiSelect]() {
|
|
if (m_bIsFolderPicker || m_pFileDialog->acceptMode() == QFileDialog::AcceptSave)
|
|
return;
|
|
|
|
m_pFileDialog->setFileMode(multiSelect ? QFileDialog::ExistingFiles
|
|
: QFileDialog::ExistingFile);
|
|
});
|
|
}
|
|
|
|
void SAL_CALL Qt5FilePicker::setDefaultName(const OUString& name)
|
|
{
|
|
SolarMutexGuard g;
|
|
auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
pSalInst->RunInMainThread([this, &name]() { m_pFileDialog->selectFile(toQString(name)); });
|
|
}
|
|
|
|
void SAL_CALL Qt5FilePicker::setDisplayDirectory(const OUString& dir)
|
|
{
|
|
SolarMutexGuard g;
|
|
auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
pSalInst->RunInMainThread([this, &dir]() {
|
|
QString qDir(toQString(dir));
|
|
m_pFileDialog->setDirectoryUrl(QUrl(qDir));
|
|
});
|
|
}
|
|
|
|
OUString SAL_CALL Qt5FilePicker::getDisplayDirectory()
|
|
{
|
|
SolarMutexGuard g;
|
|
OUString ret;
|
|
auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
pSalInst->RunInMainThread(
|
|
[&ret, this]() { ret = toOUString(m_pFileDialog->directoryUrl().toString()); });
|
|
return ret;
|
|
}
|
|
|
|
uno::Sequence<OUString> SAL_CALL Qt5FilePicker::getFiles()
|
|
{
|
|
uno::Sequence<OUString> seq = getSelectedFiles();
|
|
if (seq.getLength() > 1)
|
|
seq.realloc(1);
|
|
return seq;
|
|
}
|
|
|
|
uno::Sequence<OUString> SAL_CALL Qt5FilePicker::getSelectedFiles()
|
|
{
|
|
SolarMutexGuard g;
|
|
QList<QUrl> urls;
|
|
auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
pSalInst->RunInMainThread([&urls, this]() { urls = m_pFileDialog->selectedUrls(); });
|
|
|
|
uno::Sequence<OUString> seq(urls.size());
|
|
|
|
auto const trans = css::uri::ExternalUriReferenceTranslator::create(m_context);
|
|
size_t i = 0;
|
|
for (const QUrl& aURL : urls)
|
|
{
|
|
// Unlike LO, QFileDialog (<https://doc.qt.io/qt-5/qfiledialog.html>) apparently always
|
|
// treats file-system pathnames as UTF-8--encoded, regardless of LANG/LC_CTYPE locale
|
|
// setting. And pathnames containing byte sequences that are not valid UTF-8 are apparently
|
|
// filtered out and not even displayed by QFileDialog, so aURL will always have a "payload"
|
|
// that matches the pathname's byte sequence. So the pathname's byte sequence (which
|
|
// happens to also be aURL's payload) in the LANG/LC_CTYPE encoding needs to be converted
|
|
// into LO's internal UTF-8 file URL encoding via
|
|
// XExternalUriReferenceTranslator::translateToInternal (which looks somewhat paradoxical as
|
|
// aURL.toEncoded() nominally already has a UTF-8 payload):
|
|
auto const extUrl = toOUString(aURL.toEncoded());
|
|
auto intUrl = trans->translateToInternal(extUrl);
|
|
if (intUrl.isEmpty())
|
|
{
|
|
// If translation failed, fall back to original URL:
|
|
SAL_WARN("vcl.qt5", "cannot convert <" << extUrl << "> from locale encoding to UTF-8");
|
|
intUrl = extUrl;
|
|
}
|
|
seq[i++] = intUrl;
|
|
}
|
|
|
|
return seq;
|
|
}
|
|
|
|
void SAL_CALL Qt5FilePicker::appendFilter(const OUString& title, const OUString& filter)
|
|
{
|
|
SolarMutexGuard g;
|
|
auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
if (!pSalInst->IsMainThread())
|
|
{
|
|
pSalInst->RunInMainThread([this, &title, &filter]() { appendFilter(title, filter); });
|
|
return;
|
|
}
|
|
|
|
// '/' need to be escaped else they are assumed to be mime types
|
|
QString sTitle = toQString(title).replace("/", "\\/");
|
|
|
|
QString sFilterName = sTitle;
|
|
// the Qt5 non-native file picker adds the extensions to the filter title, so strip them
|
|
if (m_pFileDialog->testOption(QFileDialog::DontUseNativeDialog))
|
|
{
|
|
int pos = sFilterName.indexOf(" (");
|
|
if (pos >= 0)
|
|
sFilterName.truncate(pos);
|
|
}
|
|
|
|
QString sGlobFilter = toQString(filter);
|
|
|
|
// LibreOffice gives us filters separated by ';' qt dialogs just want space separated
|
|
sGlobFilter.replace(";", " ");
|
|
|
|
// make sure "*.*" is not used as "all files"
|
|
sGlobFilter.replace("*.*", "*");
|
|
|
|
m_aNamedFilterList << QStringLiteral("%1 (%2)").arg(sFilterName, sGlobFilter);
|
|
m_aTitleToFilterMap[sTitle] = m_aNamedFilterList.constLast();
|
|
m_aNamedFilterToExtensionMap[m_aNamedFilterList.constLast()] = sGlobFilter;
|
|
}
|
|
|
|
void SAL_CALL Qt5FilePicker::setCurrentFilter(const OUString& title)
|
|
{
|
|
SolarMutexGuard g;
|
|
auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
pSalInst->RunInMainThread([this, &title]() {
|
|
m_aCurrentFilter = m_aTitleToFilterMap.value(toQString(title).replace("/", "\\/"));
|
|
});
|
|
}
|
|
|
|
OUString SAL_CALL Qt5FilePicker::getCurrentFilter()
|
|
{
|
|
SolarMutexGuard g;
|
|
QString filter;
|
|
auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
pSalInst->RunInMainThread([&filter, this]() {
|
|
filter = m_aTitleToFilterMap.key(m_pFileDialog->selectedNameFilter());
|
|
});
|
|
|
|
if (filter.isEmpty())
|
|
filter = "ODF Text Document (.odt)";
|
|
return toOUString(filter);
|
|
}
|
|
|
|
void SAL_CALL Qt5FilePicker::appendFilterGroup(const OUString& rGroupTitle,
|
|
const uno::Sequence<beans::StringPair>& filters)
|
|
{
|
|
SolarMutexGuard g;
|
|
auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
if (!pSalInst->IsMainThread())
|
|
{
|
|
pSalInst->RunInMainThread(
|
|
[this, &rGroupTitle, &filters]() { appendFilterGroup(rGroupTitle, filters); });
|
|
return;
|
|
}
|
|
|
|
const sal_uInt16 length = filters.getLength();
|
|
for (sal_uInt16 i = 0; i < length; ++i)
|
|
{
|
|
beans::StringPair aPair = filters[i];
|
|
appendFilter(aPair.First, aPair.Second);
|
|
}
|
|
}
|
|
|
|
uno::Any Qt5FilePicker::handleGetListValue(const QComboBox* pWidget, sal_Int16 nControlAction)
|
|
{
|
|
uno::Any aAny;
|
|
switch (nControlAction)
|
|
{
|
|
case ControlActions::GET_ITEMS:
|
|
{
|
|
Sequence<OUString> aItemList(pWidget->count());
|
|
for (sal_Int32 i = 0; i < pWidget->count(); ++i)
|
|
aItemList[i] = toOUString(pWidget->itemText(i));
|
|
aAny <<= aItemList;
|
|
break;
|
|
}
|
|
case ControlActions::GET_SELECTED_ITEM:
|
|
{
|
|
if (!pWidget->currentText().isEmpty())
|
|
aAny <<= toOUString(pWidget->currentText());
|
|
break;
|
|
}
|
|
case ControlActions::GET_SELECTED_ITEM_INDEX:
|
|
{
|
|
if (pWidget->currentIndex() >= 0)
|
|
aAny <<= static_cast<sal_Int32>(pWidget->currentIndex());
|
|
break;
|
|
}
|
|
default:
|
|
SAL_WARN("vcl.qt5",
|
|
"undocumented/unimplemented ControlAction for a list " << nControlAction);
|
|
break;
|
|
}
|
|
return aAny;
|
|
}
|
|
|
|
void Qt5FilePicker::handleSetListValue(QComboBox* pWidget, sal_Int16 nControlAction,
|
|
const uno::Any& rValue)
|
|
{
|
|
switch (nControlAction)
|
|
{
|
|
case ControlActions::ADD_ITEM:
|
|
{
|
|
OUString sItem;
|
|
rValue >>= sItem;
|
|
pWidget->addItem(toQString(sItem));
|
|
break;
|
|
}
|
|
case ControlActions::ADD_ITEMS:
|
|
{
|
|
Sequence<OUString> aStringList;
|
|
rValue >>= aStringList;
|
|
for (auto const& sItem : std::as_const(aStringList))
|
|
pWidget->addItem(toQString(sItem));
|
|
break;
|
|
}
|
|
case ControlActions::DELETE_ITEM:
|
|
{
|
|
sal_Int32 nPos = 0;
|
|
rValue >>= nPos;
|
|
pWidget->removeItem(nPos);
|
|
break;
|
|
}
|
|
case ControlActions::SET_SELECT_ITEM:
|
|
{
|
|
sal_Int32 nPos = 0;
|
|
rValue >>= nPos;
|
|
pWidget->setCurrentIndex(nPos);
|
|
break;
|
|
}
|
|
default:
|
|
SAL_WARN("vcl.qt5",
|
|
"undocumented/unimplemented ControlAction for a list " << nControlAction);
|
|
break;
|
|
}
|
|
|
|
pWidget->setEnabled(pWidget->count() > 0);
|
|
}
|
|
|
|
void SAL_CALL Qt5FilePicker::setValue(sal_Int16 controlId, sal_Int16 nControlAction,
|
|
const uno::Any& value)
|
|
{
|
|
SolarMutexGuard g;
|
|
auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
if (!pSalInst->IsMainThread())
|
|
{
|
|
pSalInst->RunInMainThread([this, controlId, nControlAction, &value]() {
|
|
setValue(controlId, nControlAction, value);
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (m_aCustomWidgetsMap.contains(controlId))
|
|
{
|
|
QWidget* widget = m_aCustomWidgetsMap.value(controlId);
|
|
QCheckBox* cb = dynamic_cast<QCheckBox*>(widget);
|
|
if (cb)
|
|
cb->setChecked(value.get<bool>());
|
|
else
|
|
{
|
|
QComboBox* combo = dynamic_cast<QComboBox*>(widget);
|
|
if (combo)
|
|
handleSetListValue(combo, nControlAction, value);
|
|
}
|
|
}
|
|
else
|
|
SAL_WARN("vcl.qt5", "set value on unknown control " << controlId);
|
|
}
|
|
|
|
uno::Any SAL_CALL Qt5FilePicker::getValue(sal_Int16 controlId, sal_Int16 nControlAction)
|
|
{
|
|
SolarMutexGuard g;
|
|
auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
if (!pSalInst->IsMainThread())
|
|
{
|
|
uno::Any ret;
|
|
pSalInst->RunInMainThread([&ret, this, controlId, nControlAction]() {
|
|
ret = getValue(controlId, nControlAction);
|
|
});
|
|
return ret;
|
|
}
|
|
|
|
uno::Any res(false);
|
|
if (m_aCustomWidgetsMap.contains(controlId))
|
|
{
|
|
QWidget* widget = m_aCustomWidgetsMap.value(controlId);
|
|
QCheckBox* cb = dynamic_cast<QCheckBox*>(widget);
|
|
if (cb)
|
|
res <<= cb->isChecked();
|
|
else
|
|
{
|
|
QComboBox* combo = dynamic_cast<QComboBox*>(widget);
|
|
if (combo)
|
|
res = handleGetListValue(combo, nControlAction);
|
|
}
|
|
}
|
|
else
|
|
SAL_WARN("vcl.qt5", "get value on unknown control " << controlId);
|
|
|
|
return res;
|
|
}
|
|
|
|
void SAL_CALL Qt5FilePicker::enableControl(sal_Int16 controlId, sal_Bool enable)
|
|
{
|
|
SolarMutexGuard g;
|
|
auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
pSalInst->RunInMainThread([this, controlId, enable]() {
|
|
if (m_aCustomWidgetsMap.contains(controlId))
|
|
m_aCustomWidgetsMap.value(controlId)->setEnabled(enable);
|
|
else
|
|
SAL_WARN("vcl.qt5", "enable unknown control " << controlId);
|
|
});
|
|
}
|
|
|
|
void SAL_CALL Qt5FilePicker::setLabel(sal_Int16 controlId, const OUString& label)
|
|
{
|
|
SolarMutexGuard g;
|
|
auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
if (!pSalInst->IsMainThread())
|
|
{
|
|
pSalInst->RunInMainThread([this, controlId, label]() { setLabel(controlId, label); });
|
|
return;
|
|
}
|
|
|
|
if (m_aCustomWidgetsMap.contains(controlId))
|
|
{
|
|
QCheckBox* cb = dynamic_cast<QCheckBox*>(m_aCustomWidgetsMap.value(controlId));
|
|
if (cb)
|
|
cb->setText(toQString(label));
|
|
}
|
|
else
|
|
SAL_WARN("vcl.qt5", "set label on unknown control " << controlId);
|
|
}
|
|
|
|
OUString SAL_CALL Qt5FilePicker::getLabel(sal_Int16 controlId)
|
|
{
|
|
SolarMutexGuard g;
|
|
auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
if (!pSalInst->IsMainThread())
|
|
{
|
|
OUString ret;
|
|
pSalInst->RunInMainThread([&ret, this, controlId]() { ret = getLabel(controlId); });
|
|
return ret;
|
|
}
|
|
|
|
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.qt5", "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(sal_Int16 controlId)
|
|
{
|
|
QWidget* widget = nullptr;
|
|
QLabel* label = nullptr;
|
|
const char* resId = nullptr;
|
|
QCheckBox* pCheckbox = 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 CHECKBOX_GPGENCRYPTION:
|
|
resId = STR_FPICKER_GPGENCRYPT;
|
|
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_IMAGE_ANCHOR:
|
|
resId = STR_FPICKER_IMAGE_ANCHOR;
|
|
break;
|
|
case LISTBOX_VERSION_LABEL:
|
|
case LISTBOX_TEMPLATE_LABEL:
|
|
case LISTBOX_IMAGE_TEMPLATE_LABEL:
|
|
case LISTBOX_IMAGE_ANCHOR_LABEL:
|
|
case LISTBOX_FILTER_SELECTOR:
|
|
break;
|
|
}
|
|
|
|
switch (controlId)
|
|
{
|
|
case CHECKBOX_AUTOEXTENSION:
|
|
pCheckbox = new QCheckBox(getResString(resId), m_pExtraControls);
|
|
// to add/remove automatic file extension based on checkbox
|
|
connect(pCheckbox, SIGNAL(stateChanged(int)), this,
|
|
SLOT(updateAutomaticFileExtension()));
|
|
widget = pCheckbox;
|
|
break;
|
|
case CHECKBOX_PASSWORD:
|
|
case CHECKBOX_FILTEROPTIONS:
|
|
case CHECKBOX_READONLY:
|
|
case CHECKBOX_LINK:
|
|
case CHECKBOX_PREVIEW:
|
|
case CHECKBOX_SELECTION:
|
|
case CHECKBOX_GPGENCRYPTION:
|
|
widget = new QCheckBox(getResString(resId), m_pExtraControls);
|
|
break;
|
|
case PUSHBUTTON_PLAY:
|
|
break;
|
|
case LISTBOX_VERSION:
|
|
case LISTBOX_TEMPLATE:
|
|
case LISTBOX_IMAGE_ANCHOR:
|
|
case LISTBOX_IMAGE_TEMPLATE:
|
|
case LISTBOX_FILTER_SELECTOR:
|
|
label = new QLabel(getResString(resId), m_pExtraControls);
|
|
widget = new QComboBox(m_pExtraControls);
|
|
label->setBuddy(widget);
|
|
break;
|
|
case LISTBOX_VERSION_LABEL:
|
|
case LISTBOX_TEMPLATE_LABEL:
|
|
case LISTBOX_IMAGE_TEMPLATE_LABEL:
|
|
case LISTBOX_IMAGE_ANCHOR_LABEL:
|
|
break;
|
|
}
|
|
|
|
if (widget)
|
|
{
|
|
const int row = m_pLayout->rowCount();
|
|
if (label)
|
|
m_pLayout->addWidget(label, row, 0);
|
|
m_pLayout->addWidget(widget, row, 1);
|
|
m_aCustomWidgetsMap.insert(controlId, widget);
|
|
}
|
|
}
|
|
|
|
void SAL_CALL Qt5FilePicker::initialize(const uno::Sequence<uno::Any>& args)
|
|
{
|
|
// 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);
|
|
}
|
|
|
|
SolarMutexGuard g;
|
|
auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
if (!pSalInst->IsMainThread())
|
|
{
|
|
pSalInst->RunInMainThread([this, args]() { initialize(args); });
|
|
return;
|
|
}
|
|
|
|
m_aNamedFilterToExtensionMap.clear();
|
|
m_aNamedFilterList.clear();
|
|
m_aTitleToFilterMap.clear();
|
|
m_aCurrentFilter.clear();
|
|
|
|
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(CHECKBOX_AUTOEXTENSION);
|
|
break;
|
|
|
|
case FILESAVE_AUTOEXTENSION_PASSWORD:
|
|
acceptMode = QFileDialog::AcceptSave;
|
|
addCustomControl(CHECKBOX_AUTOEXTENSION);
|
|
addCustomControl(CHECKBOX_PASSWORD);
|
|
addCustomControl(CHECKBOX_GPGENCRYPTION);
|
|
break;
|
|
|
|
case FILESAVE_AUTOEXTENSION_PASSWORD_FILTEROPTIONS:
|
|
acceptMode = QFileDialog::AcceptSave;
|
|
addCustomControl(CHECKBOX_AUTOEXTENSION);
|
|
addCustomControl(CHECKBOX_PASSWORD);
|
|
addCustomControl(CHECKBOX_GPGENCRYPTION);
|
|
addCustomControl(CHECKBOX_FILTEROPTIONS);
|
|
break;
|
|
|
|
case FILESAVE_AUTOEXTENSION_SELECTION:
|
|
acceptMode = QFileDialog::AcceptSave;
|
|
addCustomControl(CHECKBOX_AUTOEXTENSION);
|
|
addCustomControl(CHECKBOX_SELECTION);
|
|
break;
|
|
|
|
case FILESAVE_AUTOEXTENSION_TEMPLATE:
|
|
acceptMode = QFileDialog::AcceptSave;
|
|
addCustomControl(CHECKBOX_AUTOEXTENSION);
|
|
addCustomControl(LISTBOX_TEMPLATE);
|
|
break;
|
|
|
|
case FILEOPEN_LINK_PREVIEW_IMAGE_TEMPLATE:
|
|
addCustomControl(CHECKBOX_LINK);
|
|
addCustomControl(CHECKBOX_PREVIEW);
|
|
addCustomControl(LISTBOX_IMAGE_TEMPLATE);
|
|
break;
|
|
|
|
case FILEOPEN_LINK_PREVIEW_IMAGE_ANCHOR:
|
|
addCustomControl(CHECKBOX_LINK);
|
|
addCustomControl(CHECKBOX_PREVIEW);
|
|
addCustomControl(LISTBOX_IMAGE_ANCHOR);
|
|
break;
|
|
|
|
case FILEOPEN_PLAY:
|
|
addCustomControl(PUSHBUTTON_PLAY);
|
|
break;
|
|
|
|
case FILEOPEN_LINK_PLAY:
|
|
addCustomControl(CHECKBOX_LINK);
|
|
addCustomControl(PUSHBUTTON_PLAY);
|
|
break;
|
|
|
|
case FILEOPEN_READONLY_VERSION:
|
|
addCustomControl(CHECKBOX_READONLY);
|
|
addCustomControl(LISTBOX_VERSION);
|
|
break;
|
|
|
|
case FILEOPEN_LINK_PREVIEW:
|
|
addCustomControl(CHECKBOX_LINK);
|
|
addCustomControl(CHECKBOX_PREVIEW);
|
|
break;
|
|
|
|
case FILEOPEN_PREVIEW:
|
|
addCustomControl(CHECKBOX_PREVIEW);
|
|
break;
|
|
|
|
default:
|
|
throw lang::IllegalArgumentException("Unknown template",
|
|
static_cast<XFilePicker2*>(this), 1);
|
|
}
|
|
|
|
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));
|
|
|
|
css::uno::Reference<css::awt::XWindow> xParentWindow;
|
|
if (args.getLength() > 1)
|
|
args[1] >>= xParentWindow;
|
|
if (xParentWindow.is())
|
|
{
|
|
css::uno::Reference<css::awt::XSystemDependentWindowPeer> xSysWinPeer(xParentWindow,
|
|
css::uno::UNO_QUERY);
|
|
if (xSysWinPeer.is())
|
|
{
|
|
// the sal_*Int8 handling is strange, but it's public API - no way around
|
|
css::uno::Sequence<sal_Int8> aProcessIdent(16);
|
|
rtl_getGlobalProcessId(reinterpret_cast<sal_uInt8*>(aProcessIdent.getArray()));
|
|
uno::Any aAny = xSysWinPeer->getWindowHandle(
|
|
aProcessIdent, css::lang::SystemDependent::SYSTEM_XWINDOW);
|
|
css::awt::SystemDependentXWindow xSysWin;
|
|
aAny >>= xSysWin;
|
|
|
|
const auto& pFrames = pSalInst->getFrames();
|
|
const long aWindowHandle = xSysWin.WindowHandle;
|
|
const auto it = std::find_if(pFrames.begin(), pFrames.end(),
|
|
[&aWindowHandle](auto pFrame) -> bool {
|
|
const SystemEnvData* pData = pFrame->GetSystemData();
|
|
return pData && long(pData->aWindow) == aWindowHandle;
|
|
});
|
|
if (it != pFrames.end())
|
|
m_pParentWidget = static_cast<Qt5Frame*>(*it)->asChild();
|
|
}
|
|
}
|
|
}
|
|
|
|
void SAL_CALL Qt5FilePicker::cancel() { m_pFileDialog->reject(); }
|
|
|
|
void SAL_CALL Qt5FilePicker::disposing(const lang::EventObject& rEvent)
|
|
{
|
|
uno::Reference<XFilePickerListener> xFilePickerListener(rEvent.Source, uno::UNO_QUERY);
|
|
|
|
if (xFilePickerListener.is())
|
|
{
|
|
removeFilePickerListener(xFilePickerListener);
|
|
}
|
|
}
|
|
|
|
void SAL_CALL Qt5FilePicker::queryTermination(const css::lang::EventObject&)
|
|
{
|
|
throw css::frame::TerminationVetoException();
|
|
}
|
|
|
|
void SAL_CALL Qt5FilePicker::notifyTermination(const css::lang::EventObject&)
|
|
{
|
|
SolarMutexGuard aGuard;
|
|
m_pFileDialog->reject();
|
|
}
|
|
|
|
OUString SAL_CALL Qt5FilePicker::getImplementationName()
|
|
{
|
|
return "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::updateAutomaticFileExtension()
|
|
{
|
|
bool bSetAutoExtension
|
|
= getValue(CHECKBOX_AUTOEXTENSION, ControlActions::GET_SELECTED_ITEM).get<bool>();
|
|
if (bSetAutoExtension)
|
|
{
|
|
QString sSuffix = m_aNamedFilterToExtensionMap.value(m_pFileDialog->selectedNameFilter());
|
|
// string is "*.<SUFFIX>" if a specific filter was selected that has exactly one possible file extension
|
|
if (sSuffix.lastIndexOf("*.") == 0)
|
|
{
|
|
sSuffix = sSuffix.remove("*.");
|
|
m_pFileDialog->setDefaultSuffix(sSuffix);
|
|
}
|
|
else
|
|
{
|
|
// fall back to setting none otherwise
|
|
SAL_INFO(
|
|
"vcl.qt5",
|
|
"Unable to retrieve unambiguous file extension. Will not add any automatically.");
|
|
bSetAutoExtension = false;
|
|
}
|
|
}
|
|
|
|
if (!bSetAutoExtension)
|
|
m_pFileDialog->setDefaultSuffix("");
|
|
}
|
|
|
|
void Qt5FilePicker::filterSelected(const QString&)
|
|
{
|
|
FilePickerEvent aEvent;
|
|
aEvent.ElementId = LISTBOX_FILTER;
|
|
SAL_INFO("vcl.qt5", "filter changed");
|
|
if (m_xListener.is())
|
|
m_xListener->controlStateChanged(aEvent);
|
|
}
|
|
|
|
void Qt5FilePicker::currentChanged(const QString&)
|
|
{
|
|
FilePickerEvent aEvent;
|
|
SAL_INFO("vcl.qt5", "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: */
|